]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dnssdutil.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / dnssdutil.c
1 /*
2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
3
4 dnssdutil is a command-line utility for testing the DNS-SD API.
5 */
6
7 #include "DNSMessage.h"
8
9 #include <CoreUtils/CoreUtils.h>
10 #include <dns_sd.h>
11 #include <dns_sd_private.h>
12
13 #include CF_RUNTIME_HEADER
14
15 #if( TARGET_OS_DARWIN )
16 #include <CFNetwork/CFHost.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <SystemConfiguration/SCPrivate.h>
19 #include <dnsinfo.h>
20 #include <libproc.h>
21 #include <netdb.h>
22 #include <pcap.h>
23 #include <spawn.h>
24 #include <sys/proc_info.h>
25 #include <xpc/xpc.h>
26 #endif
27
28 #if( TARGET_OS_POSIX )
29 #include <sys/resource.h>
30 #endif
31
32 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
33 #define DNSSDUTIL_INCLUDE_DNSCRYPT 0
34 #endif
35
36 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
37 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
38 #endif
39
40 #if( !defined( MDNSRESPONDER_PROJECT ) )
41 #define MDNSRESPONDER_PROJECT 0
42 #endif
43
44 #if( MDNSRESPONDER_PROJECT )
45 #include <dns_services.h>
46 #include "mdns_private.h"
47 #include "TestUtils.h"
48 #endif
49
50 //===========================================================================================================================
51 // Versioning
52 //===========================================================================================================================
53
54 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
55
56 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
57 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
58 #endif
59
60 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
61
62 //===========================================================================================================================
63 // DNS-SD
64 //===========================================================================================================================
65
66 // DNS-SD API flag descriptors
67
68 #define kDNSServiceFlagsDescriptors \
69 "\x00" "AutoTrigger\0" \
70 "\x01" "Add\0" \
71 "\x02" "Default\0" \
72 "\x03" "NoAutoRename\0" \
73 "\x04" "Shared\0" \
74 "\x05" "Unique\0" \
75 "\x06" "BrowseDomains\0" \
76 "\x07" "RegistrationDomains\0" \
77 "\x08" "LongLivedQuery\0" \
78 "\x09" "AllowRemoteQuery\0" \
79 "\x0A" "ForceMulticast\0" \
80 "\x0B" "KnownUnique\0" \
81 "\x0C" "ReturnIntermediates\0" \
82 "\x0D" "DenyConstrained\0" \
83 "\x0E" "ShareConnection\0" \
84 "\x0F" "SuppressUnusable\0" \
85 "\x10" "Timeout\0" \
86 "\x11" "IncludeP2P\0" \
87 "\x12" "WakeOnResolve\0" \
88 "\x13" "BackgroundTrafficClass\0" \
89 "\x14" "IncludeAWDL\0" \
90 "\x15" "Validate\0" \
91 "\x16" "UnicastResponse\0" \
92 "\x17" "ValidateOptional\0" \
93 "\x18" "WakeOnlyService\0" \
94 "\x19" "ThresholdOne\0" \
95 "\x1A" "ThresholdFinder\0" \
96 "\x1B" "DenyCellular\0" \
97 "\x1C" "ServiceIndex\0" \
98 "\x1D" "DenyExpensive\0" \
99 "\x1E" "PathEvaluationDone\0" \
100 "\x1F" "AllowExpiredAnswers\0" \
101 "\x00"
102
103 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
104
105 #define kDNSServiceProtocolDescriptors \
106 "\x00" "IPv4\0" \
107 "\x01" "IPv6\0" \
108 "\x04" "UDP\0" \
109 "\x05" "TCP\0" \
110 "\x00"
111
112 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
113
114 //===========================================================================================================================
115 // DNS
116 //===========================================================================================================================
117
118 #define kDNSPort 53
119 #define kDNSMaxUDPMessageSize 512
120 #define kDNSMaxTCPMessageSize UINT16_MAX
121
122 #define kDNSRecordDataLengthMax UINT16_MAX
123
124 //===========================================================================================================================
125 // mDNS
126 //===========================================================================================================================
127
128 #define kMDNSPort 5353
129
130 #define kDefaultMDNSMessageID 0
131 #define kDefaultMDNSQueryFlags 0
132
133 #define kQClassUnicastResponseBit ( 1U << 15 )
134 #define kRRClassCacheFlushBit ( 1U << 15 )
135
136 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
137
138 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
139 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
140
141 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
142
143 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
144
145 #define kLocalStr "\x05" "local"
146 #define kLocalLabel ( (const uint8_t *) kLocalStr )
147 #define kLocalName ( (const uint8_t *) kLocalStr )
148 #define kLocalNameLen sizeof( kLocalStr )
149
150 //===========================================================================================================================
151 // Test Address Blocks
152 //===========================================================================================================================
153
154 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
155
156 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
157
158 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
159
160 static const uint8_t kDNSServerBaseAddrV6[] =
161 {
162 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
163 };
164
165 static const uint8_t kMDNSReplierBaseAddrV6[] =
166 {
167 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
168 };
169
170 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
171 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
172
173 // Bad IPv4 and IPv6 Address Blocks
174 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
175 // in 203.0.113.0/24 nor 2001:db8:1::/120.
176
177 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
178
179 static const uint8_t kDNSServerBadBaseAddrV6[] =
180 {
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
182 };
183
184 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
185
186 //===========================================================================================================================
187 // Soft Linking
188 //===========================================================================================================================
189
190 #if( TARGET_OS_DARWIN )
191 SOFT_LINK_LIBRARY_EX( "/usr/lib/system", system_dnssd );
192 SOFT_LINK_FUNCTION_EX( system_dnssd, DNSServiceSleepKeepalive_sockaddr,
193 DNSServiceErrorType, (
194 DNSServiceRef * sdRef,
195 DNSServiceFlags flags,
196 const struct sockaddr * localAddr,
197 const struct sockaddr * remoteAddr,
198 unsigned int timeout,
199 DNSServiceSleepKeepaliveReply callBack,
200 void * context ),
201 ( sdRef, flags, localAddr, remoteAddr, timeout, callBack, context ) );
202 #endif
203
204 //===========================================================================================================================
205 // Misc.
206 //===========================================================================================================================
207
208 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
209 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
210
211 #if( !defined( kWhiteSpaceCharSet ) )
212 #define kWhiteSpaceCharSet "\t\n\v\f\r "
213 #endif
214
215 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
216
217 #if( !defined( strcpy_literal ) )
218 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
219 #endif
220
221 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
222 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
223
224 #define kNoSuchRecordStr "No Such Record"
225 #define kNoSuchRecordAStr "No Such Record (A)"
226 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
227
228 #define kRootLabel ( (const uint8_t *) "" )
229
230 //===========================================================================================================================
231 // Gerneral Command Options
232 //===========================================================================================================================
233
234 // Command option macros
235
236 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
237 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
238 (SHORT_HELP), NULL )
239
240 #define kRequiredOptionSuffix " [REQUIRED]"
241
242 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
243 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
244 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
245 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
246
247 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
248 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
249
250 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
251 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
252 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
253 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
254
255 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
256 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
257 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
258 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
259
260 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
261 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
262
263 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
264 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
265 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
266 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
267
268 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
269 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
270
271 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
272 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
273 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
274 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
275
276 // DNS-SD API flag options
277
278 static int gDNSSDFlags = 0;
279 static int gDNSSDFlag_AllowExpiredAnswers = false;
280 static int gDNSSDFlag_BrowseDomains = false;
281 static int gDNSSDFlag_DenyCellular = false;
282 static int gDNSSDFlag_DenyConstrained = false;
283 static int gDNSSDFlag_DenyExpensive = false;
284 static int gDNSSDFlag_ForceMulticast = false;
285 static int gDNSSDFlag_IncludeAWDL = false;
286 static int gDNSSDFlag_KnownUnique = false;
287 static int gDNSSDFlag_NoAutoRename = false;
288 static int gDNSSDFlag_PathEvaluationDone = false;
289 static int gDNSSDFlag_RegistrationDomains = false;
290 static int gDNSSDFlag_ReturnIntermediates = false;
291 static int gDNSSDFlag_Shared = false;
292 static int gDNSSDFlag_SuppressUnusable = false;
293 static int gDNSSDFlag_Timeout = false;
294 static int gDNSSDFlag_UnicastResponse = false;
295 static int gDNSSDFlag_Unique = false;
296 static int gDNSSDFlag_WakeOnResolve = false;
297
298 #define DNSSDFlagsOption() \
299 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
300 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
301
302 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
303 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
304
305 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
306 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
307 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
308 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
309 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
310 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
311 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
312 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
313 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
314 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
315 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
316 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
317 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
318 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
319 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
320 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
321
322 // Interface option
323
324 static const char * gInterface = NULL;
325
326 #define InterfaceOption() \
327 StringOption( 'i', "interface", &gInterface, "interface", \
328 "Network interface by name or index. Use index -1 for local-only.", false )
329
330 // Connection options
331
332 #define kConnectionArg_Normal ""
333 #define kConnectionArgPrefix_PID "pid:"
334 #define kConnectionArgPrefix_UUID "uuid:"
335
336 static const char * gConnectionOpt = kConnectionArg_Normal;
337
338 #define ConnectionOptions() \
339 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
340 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
341 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
342
343 #define kConnectionSection_Name "Connection Option"
344 #define kConnectionSection_Text \
345 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
346 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
347 "specifying the connection option without an argument, i.e.,\n" \
348 "\n" \
349 " --connection\n" \
350 "\n" \
351 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
352 "\n" \
353 " --connection=pid:<PID>\n" \
354 "\n" \
355 "to specify the delegator by PID, or use\n" \
356 "\n" \
357 " --connection=uuid:<UUID>\n" \
358 "\n" \
359 "to specify the delegator by UUID.\n" \
360 "\n" \
361 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
362 "\n" \
363 " --no-connection\n"
364
365 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
366
367 // Help text for record data options
368
369 #define kRDataArgPrefix_Domain "domain:"
370 #define kRDataArgPrefix_File "file:"
371 #define kRDataArgPrefix_HexString "hex:"
372 #define kRDataArgPrefix_IPv4 "ipv4:"
373 #define kRDataArgPrefix_IPv6 "ipv6:"
374 #define kRDataArgPrefix_SRV "srv:"
375 #define kRDataArgPrefix_String "string:"
376 #define kRDataArgPrefix_TXT "txt:"
377
378 #define kRecordDataSection_Name "Record Data Arguments"
379 #define kRecordDataSection_Text \
380 "A record data argument is specified in one of the following formats:\n" \
381 "\n" \
382 "Format Syntax Example\n" \
383 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
384 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
385 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
386 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
387 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
388 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
389 "String string:<string> string:'\\x09color=red'\n" \
390 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
391 "\n" \
392 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
393
394 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
395
396 //===========================================================================================================================
397 // Output Formatting
398 //===========================================================================================================================
399
400 #define kOutputFormatStr_JSON "json"
401 #define kOutputFormatStr_XML "xml"
402 #define kOutputFormatStr_Binary "binary"
403
404 typedef enum
405 {
406 kOutputFormatType_Invalid = 0,
407 kOutputFormatType_JSON = 1,
408 kOutputFormatType_XML = 2,
409 kOutputFormatType_Binary = 3
410
411 } OutputFormatType;
412
413 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
414 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
415 "\n" \
416 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
417 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
418 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
419 "\n" \
420 )
421
422 //===========================================================================================================================
423 // Browse Command Options
424 //===========================================================================================================================
425
426 static char ** gBrowse_ServiceTypes = NULL;
427 static size_t gBrowse_ServiceTypesCount = 0;
428 static const char * gBrowse_Domain = NULL;
429 static int gBrowse_DoResolve = false;
430 static int gBrowse_QueryTXT = false;
431 static int gBrowse_TimeLimitSecs = 0;
432
433 static CLIOption kBrowseOpts[] =
434 {
435 InterfaceOption(),
436 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
437 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
438
439 CLI_OPTION_GROUP( "Flags" ),
440 DNSSDFlagsOption(),
441 DNSSDFlagsOption_IncludeAWDL(),
442
443 CLI_OPTION_GROUP( "Operation" ),
444 ConnectionOptions(),
445 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
446 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
447 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
448
449 ConnectionSection(),
450 CLI_OPTION_END()
451 };
452
453 //===========================================================================================================================
454 // GetAddrInfo Command Options
455 //===========================================================================================================================
456
457 static const char * gGetAddrInfo_Name = NULL;
458 static int gGetAddrInfo_ProtocolIPv4 = false;
459 static int gGetAddrInfo_ProtocolIPv6 = false;
460 static int gGetAddrInfo_OneShot = false;
461 static int gGetAddrInfo_TimeLimitSecs = 0;
462
463 static CLIOption kGetAddrInfoOpts[] =
464 {
465 InterfaceOption(),
466 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
467 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
468 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
469
470 CLI_OPTION_GROUP( "Flags" ),
471 DNSSDFlagsOption(),
472 DNSSDFlagsOption_AllowExpiredAnswers(),
473 DNSSDFlagsOption_DenyCellular(),
474 DNSSDFlagsOption_DenyConstrained(),
475 DNSSDFlagsOption_DenyExpensive(),
476 DNSSDFlagsOption_PathEvalDone(),
477 DNSSDFlagsOption_ReturnIntermediates(),
478 DNSSDFlagsOption_SuppressUnusable(),
479 DNSSDFlagsOption_Timeout(),
480
481 CLI_OPTION_GROUP( "Operation" ),
482 ConnectionOptions(),
483 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
484 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
485
486 ConnectionSection(),
487 CLI_OPTION_END()
488 };
489
490 //===========================================================================================================================
491 // QueryRecord Command Options
492 //===========================================================================================================================
493
494 static const char * gQueryRecord_Name = NULL;
495 static const char * gQueryRecord_Type = NULL;
496 static int gQueryRecord_OneShot = false;
497 static int gQueryRecord_TimeLimitSecs = 0;
498 static int gQueryRecord_RawRData = false;
499
500 static CLIOption kQueryRecordOpts[] =
501 {
502 InterfaceOption(),
503 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
504 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
505
506 CLI_OPTION_GROUP( "Flags" ),
507 DNSSDFlagsOption(),
508 DNSSDFlagsOption_AllowExpiredAnswers(),
509 DNSSDFlagsOption_DenyCellular(),
510 DNSSDFlagsOption_DenyConstrained(),
511 DNSSDFlagsOption_DenyExpensive(),
512 DNSSDFlagsOption_ForceMulticast(),
513 DNSSDFlagsOption_IncludeAWDL(),
514 DNSSDFlagsOption_PathEvalDone(),
515 DNSSDFlagsOption_ReturnIntermediates(),
516 DNSSDFlagsOption_SuppressUnusable(),
517 DNSSDFlagsOption_Timeout(),
518 DNSSDFlagsOption_UnicastResponse(),
519
520 CLI_OPTION_GROUP( "Operation" ),
521 ConnectionOptions(),
522 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
523 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
524 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
525
526 ConnectionSection(),
527 CLI_OPTION_END()
528 };
529
530 //===========================================================================================================================
531 // Register Command Options
532 //===========================================================================================================================
533
534 static const char * gRegister_Name = NULL;
535 static const char * gRegister_Type = NULL;
536 static const char * gRegister_Domain = NULL;
537 static int gRegister_Port = 0;
538 static const char * gRegister_TXT = NULL;
539 static int gRegister_LifetimeMs = -1;
540 static const char ** gAddRecord_Types = NULL;
541 static size_t gAddRecord_TypesCount = 0;
542 static const char ** gAddRecord_Data = NULL;
543 static size_t gAddRecord_DataCount = 0;
544 static const char ** gAddRecord_TTLs = NULL;
545 static size_t gAddRecord_TTLsCount = 0;
546 static const char * gUpdateRecord_Data = NULL;
547 static int gUpdateRecord_DelayMs = 0;
548 static int gUpdateRecord_TTL = 0;
549
550 static CLIOption kRegisterOpts[] =
551 {
552 InterfaceOption(),
553 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
554 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
555 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
556 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
557 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
558
559 CLI_OPTION_GROUP( "Flags" ),
560 DNSSDFlagsOption(),
561 DNSSDFlagsOption_IncludeAWDL(),
562 DNSSDFlagsOption_KnownUnique(),
563 DNSSDFlagsOption_NoAutoRename(),
564
565 CLI_OPTION_GROUP( "Operation" ),
566 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
567
568 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
569 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
570 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
571 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
572
573 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
574 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
575 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
576 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
577
578 RecordDataSection(),
579 CLI_OPTION_END()
580 };
581
582 //===========================================================================================================================
583 // RegisterRecord Command Options
584 //===========================================================================================================================
585
586 static const char * gRegisterRecord_Name = NULL;
587 static const char * gRegisterRecord_Type = NULL;
588 static const char * gRegisterRecord_Data = NULL;
589 static int gRegisterRecord_TTL = 0;
590 static int gRegisterRecord_LifetimeMs = -1;
591 static const char * gRegisterRecord_UpdateData = NULL;
592 static int gRegisterRecord_UpdateDelayMs = 0;
593 static int gRegisterRecord_UpdateTTL = 0;
594
595 static CLIOption kRegisterRecordOpts[] =
596 {
597 InterfaceOption(),
598 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
599 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
600 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
601 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
602
603 CLI_OPTION_GROUP( "Flags" ),
604 DNSSDFlagsOption(),
605 DNSSDFlagsOption_IncludeAWDL(),
606 DNSSDFlagsOption_KnownUnique(),
607 DNSSDFlagsOption_Shared(),
608 DNSSDFlagsOption_Unique(),
609
610 CLI_OPTION_GROUP( "Operation" ),
611 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
612
613 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
614 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
615 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
616 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
617
618 RecordDataSection(),
619 CLI_OPTION_END()
620 };
621
622 //===========================================================================================================================
623 // Resolve Command Options
624 //===========================================================================================================================
625
626 static char * gResolve_Name = NULL;
627 static char * gResolve_Type = NULL;
628 static char * gResolve_Domain = NULL;
629 static int gResolve_TimeLimitSecs = 0;
630
631 static CLIOption kResolveOpts[] =
632 {
633 InterfaceOption(),
634 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
635 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
636 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
637
638 CLI_OPTION_GROUP( "Flags" ),
639 DNSSDFlagsOption(),
640 DNSSDFlagsOption_ForceMulticast(),
641 DNSSDFlagsOption_IncludeAWDL(),
642 DNSSDFlagsOption_ReturnIntermediates(),
643 DNSSDFlagsOption_WakeOnResolve(),
644
645 CLI_OPTION_GROUP( "Operation" ),
646 ConnectionOptions(),
647 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
648
649 ConnectionSection(),
650 CLI_OPTION_END()
651 };
652
653 //===========================================================================================================================
654 // Reconfirm Command Options
655 //===========================================================================================================================
656
657 static const char * gReconfirmRecord_Name = NULL;
658 static const char * gReconfirmRecord_Type = NULL;
659 static const char * gReconfirmRecord_Class = NULL;
660 static const char * gReconfirmRecord_Data = NULL;
661
662 static CLIOption kReconfirmOpts[] =
663 {
664 InterfaceOption(),
665 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
666 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
667 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
668 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
669
670 CLI_OPTION_GROUP( "Flags" ),
671 DNSSDFlagsOption(),
672
673 RecordDataSection(),
674 CLI_OPTION_END()
675 };
676
677 //===========================================================================================================================
678 // getaddrinfo-POSIX Command Options
679 //===========================================================================================================================
680
681 static const char * gGAIPOSIX_HostName = NULL;
682 static const char * gGAIPOSIX_ServName = NULL;
683 static const char * gGAIPOSIX_Family = NULL;
684 static int gGAIPOSIXFlag_AddrConfig = false;
685 static int gGAIPOSIXFlag_All = false;
686 static int gGAIPOSIXFlag_CanonName = false;
687 static int gGAIPOSIXFlag_NumericHost = false;
688 static int gGAIPOSIXFlag_NumericServ = false;
689 static int gGAIPOSIXFlag_Passive = false;
690 static int gGAIPOSIXFlag_V4Mapped = false;
691 #if( defined( AI_V4MAPPED_CFG ) )
692 static int gGAIPOSIXFlag_V4MappedCFG = false;
693 #endif
694 #if( defined( AI_DEFAULT ) )
695 static int gGAIPOSIXFlag_Default = false;
696 #endif
697 #if( defined( AI_UNUSABLE ) )
698 static int gGAIPOSIXFlag_Unusable = false;
699 #endif
700
701 static CLIOption kGetAddrInfoPOSIXOpts[] =
702 {
703 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
704 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
705
706 CLI_OPTION_GROUP( "Hints" ),
707 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
708 "\n"
709 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
710 "address family is specified, then AF_UNSPEC is used.\n"
711 "\n" ),
712 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
713 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
714 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
715 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
716 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
717 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
718 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
719 #if( defined( AI_V4MAPPED_CFG ) )
720 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
721 #endif
722 #if( defined( AI_DEFAULT ) )
723 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
724 #endif
725 #if( defined( AI_UNUSABLE ) )
726 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
727 #endif
728
729 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
730 CLI_OPTION_END()
731 };
732
733 //===========================================================================================================================
734 // ReverseLookup Command Options
735 //===========================================================================================================================
736
737 static const char * gReverseLookup_IPAddr = NULL;
738 static int gReverseLookup_OneShot = false;
739 static int gReverseLookup_TimeLimitSecs = 0;
740
741 static CLIOption kReverseLookupOpts[] =
742 {
743 InterfaceOption(),
744 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
745
746 CLI_OPTION_GROUP( "Flags" ),
747 DNSSDFlagsOption(),
748 DNSSDFlagsOption_ForceMulticast(),
749 DNSSDFlagsOption_ReturnIntermediates(),
750 DNSSDFlagsOption_SuppressUnusable(),
751
752 CLI_OPTION_GROUP( "Operation" ),
753 ConnectionOptions(),
754 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
755 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
756
757 ConnectionSection(),
758 CLI_OPTION_END()
759 };
760
761 //===========================================================================================================================
762 // PortMapping Command Options
763 //===========================================================================================================================
764
765 static int gPortMapping_ProtocolTCP = false;
766 static int gPortMapping_ProtocolUDP = false;
767 static int gPortMapping_InternalPort = 0;
768 static int gPortMapping_ExternalPort = 0;
769 static int gPortMapping_TTL = 0;
770
771 static CLIOption kPortMappingOpts[] =
772 {
773 InterfaceOption(),
774 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
775 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
776 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
777 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
778 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
779
780 CLI_OPTION_GROUP( "Flags" ),
781 DNSSDFlagsOption(),
782
783 CLI_OPTION_GROUP( "Operation" ),
784 ConnectionOptions(),
785
786 ConnectionSection(),
787 CLI_OPTION_END()
788 };
789
790 #if( TARGET_OS_DARWIN )
791 //===========================================================================================================================
792 // RegisterKA Command Options
793 //===========================================================================================================================
794
795 static const char * gRegisterKA_LocalAddress = NULL;
796 static const char * gRegisterKA_RemoteAddress = NULL;
797 static int gRegisterKA_Timeout = 0;
798
799 static CLIOption kRegisterKA_Opts[] =
800 {
801 DNSSDFlagsOption(),
802 StringOption( 'l', "local", &gRegisterKA_LocalAddress, "IP addr+port", "TCP connection's local IPv4 or IPv6 address and port pair.", true ),
803 StringOption( 'r', "remote", &gRegisterKA_RemoteAddress, "IP addr+port", "TCP connection's remote IPv4 or IPv6 address and port pair.", true ),
804 IntegerOption( 't', "timeout", &gRegisterKA_Timeout, "timeout", "Keepalive record's timeout value, i.e., its 't=' value.", false ),
805 CLI_OPTION_END()
806 };
807
808 static void RegisterKACmd( void );
809 #endif
810
811 //===========================================================================================================================
812 // BrowseAll Command Options
813 //===========================================================================================================================
814
815 static const char * gBrowseAll_Domain = NULL;
816 static const char ** gBrowseAll_ServiceTypes = NULL;
817 static size_t gBrowseAll_ServiceTypesCount = 0;
818 static int gBrowseAll_BrowseTimeSecs = 5;
819 static int gBrowseAll_ConnectTimeout = 0;
820
821 static CLIOption kBrowseAllOpts[] =
822 {
823 InterfaceOption(),
824 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
825 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 ),
826
827 CLI_OPTION_GROUP( "Flags" ),
828 DNSSDFlagsOption_IncludeAWDL(),
829
830 CLI_OPTION_GROUP( "Operation" ),
831 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
832 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
833 CLI_OPTION_END()
834 };
835
836 //===========================================================================================================================
837 // GetNameInfo Command Options
838 //===========================================================================================================================
839
840 static void GetNameInfoCmd( void );
841
842 static char * gGetNameInfo_IPAddress = NULL;
843 static int gGetNameInfoFlag_DGram = false;
844 static int gGetNameInfoFlag_NameReqd = false;
845 static int gGetNameInfoFlag_NoFQDN = false;
846 static int gGetNameInfoFlag_NumericHost = false;
847 static int gGetNameInfoFlag_NumericScope = false;
848 static int gGetNameInfoFlag_NumericServ = false;
849
850 static CLIOption kGetNameInfoOpts[] =
851 {
852 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
853
854 CLI_OPTION_GROUP( "Flags" ),
855 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
856 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
857 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
858 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
859 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
860 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
861
862 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
863 CLI_OPTION_END()
864 };
865
866 //===========================================================================================================================
867 // GetAddrInfoStress Command Options
868 //===========================================================================================================================
869
870 static int gGAIStress_TestDurationSecs = 0;
871 static int gGAIStress_ConnectionCount = 0;
872 static int gGAIStress_DurationMinMs = 0;
873 static int gGAIStress_DurationMaxMs = 0;
874 static int gGAIStress_RequestCountMax = 0;
875
876 static CLIOption kGetAddrInfoStressOpts[] =
877 {
878 InterfaceOption(),
879
880 CLI_OPTION_GROUP( "Flags" ),
881 DNSSDFlagsOption_ReturnIntermediates(),
882 DNSSDFlagsOption_SuppressUnusable(),
883
884 CLI_OPTION_GROUP( "Operation" ),
885 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
886 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
887 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
888 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
889 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
890 CLI_OPTION_END()
891 };
892
893 //===========================================================================================================================
894 // DNSQuery Command Options
895 //===========================================================================================================================
896
897 static char * gDNSQuery_Name = NULL;
898 static char * gDNSQuery_Type = "A";
899 static char * gDNSQuery_Server = NULL;
900 static int gDNSQuery_TimeLimitSecs = 5;
901 static int gDNSQuery_UseTCP = false;
902 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
903 static int gDNSQuery_RawRData = false;
904 static int gDNSQuery_Verbose = false;
905
906 #if( TARGET_OS_DARWIN )
907 #define kDNSQueryServerOptionIsRequired false
908 #else
909 #define kDNSQueryServerOptionIsRequired true
910 #endif
911
912 static CLIOption kDNSQueryOpts[] =
913 {
914 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
915 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
916 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
917 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
918 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
919 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
920 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
921 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
922 CLI_OPTION_END()
923 };
924
925 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
926 //===========================================================================================================================
927 // DNSCrypt Command Options
928 //===========================================================================================================================
929
930 static char * gDNSCrypt_ProviderName = NULL;
931 static char * gDNSCrypt_ProviderKey = NULL;
932 static char * gDNSCrypt_Name = NULL;
933 static char * gDNSCrypt_Type = NULL;
934 static char * gDNSCrypt_Server = NULL;
935 static int gDNSCrypt_TimeLimitSecs = 5;
936 static int gDNSCrypt_RawRData = false;
937 static int gDNSCrypt_Verbose = false;
938
939 static CLIOption kDNSCryptOpts[] =
940 {
941 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
942 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
943 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
944 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
945 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
946 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
947 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
948 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
949 CLI_OPTION_END()
950 };
951 #endif
952
953 //===========================================================================================================================
954 // MDNSQuery Command Options
955 //===========================================================================================================================
956
957 static char * gMDNSQuery_Name = NULL;
958 static char * gMDNSQuery_Type = NULL;
959 static int gMDNSQuery_SourcePort = 0;
960 static int gMDNSQuery_IsQU = false;
961 static int gMDNSQuery_RawRData = false;
962 static int gMDNSQuery_UseIPv4 = false;
963 static int gMDNSQuery_UseIPv6 = false;
964 static int gMDNSQuery_AllResponses = false;
965 static int gMDNSQuery_ReceiveSecs = 1;
966
967 static CLIOption kMDNSQueryOpts[] =
968 {
969 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
970 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
971 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
972 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
973 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
974 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
975 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
976 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
977 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
978 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 ),
979 CLI_OPTION_END()
980 };
981
982 //===========================================================================================================================
983 // MDNSCollider Command Options
984 //===========================================================================================================================
985
986 #define kMDNSColliderProgramSection_Intro \
987 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
988 "ought to react to probe queries that match its record's name, if at all.\n" \
989 "\n" \
990 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
991 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
992 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
993 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
994 "accomplished with the following program:\n" \
995 "\n" \
996 " probes 3r; send; wait 5000\n" \
997 "\n" \
998 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
999 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
1000 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
1001 "is more than enough time for the collider to respond to probe queries.\n" \
1002 "\n" \
1003 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
1004 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
1005 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1006
1007 #define kMDNSColliderProgramSection_Probes \
1008 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
1009 "\n" \
1010 "Usage: probes [<action-string>]\n" \
1011 "\n" \
1012 "The syntax for an action-string is\n" \
1013 "\n" \
1014 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
1015 " <action> ::= [<repeat-count>] <action-code>\n" \
1016 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
1017 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
1018 "\n" \
1019 "An expanded action-string is defined as\n" \
1020 "\n" \
1021 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
1022 "\n" \
1023 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
1024 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
1025 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
1026 "are not allowed.\n" \
1027 "\n" \
1028 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
1029 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
1030 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
1031 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
1032 "\n" \
1033 " Code Action\n" \
1034 " ---- ------\n" \
1035 " n Do nothing.\n" \
1036 " r Respond to the probe query.\n" \
1037 " u Respond to the probe query via unicast.\n" \
1038 " m Respond to the probe query via multicast.\n" \
1039 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1040 "\n" \
1041 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1042 "a response nor does it multicast its own probe query.\n"
1043
1044 #define kMDNSColliderProgramSection_Send \
1045 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1046 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1047 "\n" \
1048 "Usage: send\n"
1049
1050 #define kMDNSColliderProgramSection_Wait \
1051 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1052 "\n" \
1053 "Usage: wait <milliseconds>\n"
1054
1055 #define kMDNSColliderProgramSection_Loop \
1056 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1057 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1058 "\n" \
1059 "Usage: loop <non-zero count>; ... ; done\n" \
1060 "\n" \
1061 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1062 "\n" \
1063 " loop 3; wait 1000; send; done"
1064
1065 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1066
1067 static const char * gMDNSCollider_Name = NULL;
1068 static const char * gMDNSCollider_Type = NULL;
1069 static const char * gMDNSCollider_RecordData = NULL;
1070 static int gMDNSCollider_UseIPv4 = false;
1071 static int gMDNSCollider_UseIPv6 = false;
1072 static const char * gMDNSCollider_Program = NULL;
1073
1074 static CLIOption kMDNSColliderOpts[] =
1075 {
1076 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1077 StringOption( 'n', "name", &gMDNSCollider_Name, "name", "Collider's record name.", true ),
1078 StringOption( 't', "type", &gMDNSCollider_Type, "type", "Collider's record type.", true ),
1079 StringOption( 'd', "data", &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1080 StringOption( 'p', "program", &gMDNSCollider_Program, "program", "Program to execute. See Program section below.", true ),
1081 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4, "Use IPv4." ),
1082 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6, "Use IPv6." ),
1083
1084 RecordDataSection(),
1085 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro ),
1086 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes ),
1087 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send ),
1088 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait ),
1089 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop ),
1090 CLI_OPTION_END()
1091 };
1092
1093 static void MDNSColliderCmd( void );
1094
1095 //===========================================================================================================================
1096 // PIDToUUID Command Options
1097 //===========================================================================================================================
1098
1099 static int gPIDToUUID_PID = 0;
1100
1101 static CLIOption kPIDToUUIDOpts[] =
1102 {
1103 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1104 CLI_OPTION_END()
1105 };
1106
1107 //===========================================================================================================================
1108 // DNSServer Command Options
1109 //===========================================================================================================================
1110
1111 #define kDNSServerInfoText_Intro \
1112 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1113 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1114 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1115 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1116 "\n" \
1117 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1118
1119 #define kDNSServerInfoText_NameExistence \
1120 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1121 "\n" \
1122 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1123 "order, unless otherwise noted, consist of\n" \
1124 "\n" \
1125 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1126 " 2. at most one Count label;\n" \
1127 " 3. zero or more Tag labels;\n" \
1128 " 4. at most one TTL label; and\n" \
1129 " 5. at most one IPv4 or IPv6 label.\n" \
1130 "\n" \
1131 "An SRV name is defined as a name with the following form:\n" \
1132 "\n" \
1133 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1134 "\n" \
1135 "See \"SRV Names\" for details.\n"
1136
1137 #define kDNSServerInfoText_ResourceRecords \
1138 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1139 "\n" \
1140 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1141 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1142 "\n" \
1143 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1144 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1145 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1146 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1147 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1148 "\n" \
1149 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1150 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1151 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1152 "\n" \
1153 "SRV names are names of SRV records.\n" \
1154 "\n" \
1155 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1156 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1157 "AAAA records with specific TTL values.\n"
1158
1159 #define kDNSServerInfoText_AliasLabel \
1160 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1161 "\n" \
1162 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1163 "exactly N CNAME records:\n" \
1164 "\n" \
1165 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1166 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1167 " name has \"alias-(i - 1)\" as its first label.\n" \
1168 "\n" \
1169 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1170 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1171 " \"alias\" instead.\n" \
1172 "\n" \
1173 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1174 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1175 "\n" \
1176 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1177 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1178 "QNAME minus its first label.\n" \
1179 "\n" \
1180 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1181 "records:\n" \
1182 "\n" \
1183 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1184 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1185 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1186 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1187
1188 #define kDNSServerInfoText_AliasTTLLabel \
1189 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1190 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1191 "\n" \
1192 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1193 "will contain exactly N CNAME records:\n" \
1194 "\n" \
1195 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1196 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1197 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1198 "\n" \
1199 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1200 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1201 " label.\n" \
1202 "\n" \
1203 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1204 "CNAME records:\n" \
1205 "\n" \
1206 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1207 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1208 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1209
1210 #define kDNSServerInfoText_CountLabel \
1211 "Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [1, 255] and N_2 is\n" \
1212 "an integer in [N_1, 255].\n" \
1213 "\n" \
1214 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1215 "QTYPE, then the response will contain exactly N address records:\n" \
1216 "\n" \
1217 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1218 " and whose RDATA is an address equal to a constant base address + i.\n" \
1219 "\n" \
1220 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1221 "\n" \
1222 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1223 "records:\n" \
1224 "\n" \
1225 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1226 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1227 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1228 "\n" \
1229 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1230 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1231 "\n" \
1232 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1233 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1234 "\n" \
1235 " 2. The order of the address records will be random.\n" \
1236 "\n" \
1237 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1238 "following AAAA records:\n" \
1239 "\n" \
1240 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1241 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1242 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1243 "\n" \
1244 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1245 "will contain no address records, regardless of whether it contains a Count label.\n" \
1246 "\n" \
1247 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1248 "count-1\".\n"
1249
1250 #define kDNSServerInfoText_TagLabel \
1251 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1252 "\n" \
1253 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1254 "to increase the sizes of domain names.\n"
1255
1256 #define kDNSServerInfoText_TTLLabel \
1257 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1258 "\n" \
1259 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1260 "response will have a TTL value equal to T.\n"
1261
1262 #define kDNSServerInfoText_IPv4Label \
1263 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1264
1265 #define kDNSServerInfoText_IPv6Label \
1266 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1267
1268 #define kDNSServerInfoText_SRVNames \
1269 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1270 "\n" \
1271 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1272 "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" \
1273 "the target hostname of each of the SRV name's SRV records.\n" \
1274 "\n" \
1275 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1276 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1277 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1278 "d.test. labels, whichever comes first.\n" \
1279 "\n" \
1280 "Example. A response to an SRV record query with a QNAME of\n" \
1281 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1282 "\n" \
1283 "_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" \
1284 "_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"
1285
1286 #define kDNSServerInfoText_BadUDPMode \
1287 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1288 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1289 "that's not equal to the query's message ID.\n" \
1290 "\n" \
1291 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1292 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1293 "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" \
1294 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1295 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1296
1297 static int gDNSServer_LoopbackOnly = false;
1298 static int gDNSServer_Foreground = false;
1299 static int gDNSServer_ResponseDelayMs = 0;
1300 static int gDNSServer_DefaultTTL = 60;
1301 static int gDNSServer_Port = kDNSPort;
1302 static const char * gDNSServer_DomainOverride = NULL;
1303 #if( TARGET_OS_DARWIN )
1304 static const char * gDNSServer_FollowPID = NULL;
1305 #endif
1306 static int gDNSServer_BadUDPMode = false;
1307
1308 static CLIOption kDNSServerOpts[] =
1309 {
1310 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ),
1311 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Direct log output to stdout instead of system logging." ),
1312 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1313 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1314 IntegerOption( 'p', "port", &gDNSServer_Port, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1315 StringOption( 0 , "domain", &gDNSServer_DomainOverride, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1316 #if( TARGET_OS_DARWIN )
1317 StringOption( 0 , "follow", &gDNSServer_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1318 #endif
1319 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1320
1321 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1322 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1323 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1324 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1325 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1326 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1327 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1328 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1329 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1330 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1331 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames ),
1332 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode ),
1333 CLI_OPTION_END()
1334 };
1335
1336 static void DNSServerCmd( void );
1337
1338 //===========================================================================================================================
1339 // MDNSReplier Command Options
1340 //===========================================================================================================================
1341
1342 #define kMDNSReplierPortBase 50000
1343
1344 #define kMDNSReplierInfoText_Intro \
1345 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1346 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1347 "\n" \
1348 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1349
1350 #define kMDNSReplierInfoText_Parameters \
1351 "There are five parameters that control the replier's set of authoritative records.\n" \
1352 "\n" \
1353 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1354 " parameter is specified with the --hostname option.\n" \
1355 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1356 " option.\n" \
1357 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1358 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1359 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1360 " --maxInstanceCount option.\n" \
1361 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1362 " with the --countA option.\n" \
1363 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1364 " specified with the --countAAAA option.\n"
1365
1366 #define kMDNSReplierInfoText_PTR \
1367 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1368 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1369 "\n" \
1370 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1371 "\n" \
1372 " 1. The first PTR record is defined as\n" \
1373 "\n" \
1374 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1375 " TYPE: PTR\n" \
1376 " CLASS: IN\n" \
1377 " TTL: 4500\n" \
1378 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1379 "\n" \
1380 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1381 "\n" \
1382 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1383 " TYPE: PTR\n" \
1384 " CLASS: IN\n" \
1385 " TTL: 4500\n" \
1386 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1387
1388 #define kMDNSReplierInfoText_SRV \
1389 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1390 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1391 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1392 "\n" \
1393 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1394 "\n" \
1395 " 1. The first SRV record is defined as\n" \
1396 "\n" \
1397 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1398 " TYPE: SRV\n" \
1399 " CLASS: IN\n" \
1400 " TTL: 120\n" \
1401 " RDATA:\n" \
1402 " Priority: 0\n" \
1403 " Weight: 0\n" \
1404 " Port: (50000 + L) mod 2^16\n" \
1405 " Target: <hostname>.local.\n" \
1406 "\n" \
1407 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1408 "\n" \
1409 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1410 " TYPE: SRV\n" \
1411 " CLASS: IN\n" \
1412 " TTL: 120\n" \
1413 " RDATA:\n" \
1414 " Priority: 0\n" \
1415 " Weight: 0\n" \
1416 " Port: (50000 + L) mod 2^16\n" \
1417 " Target: <hostname>-<i>.local.\n"
1418
1419 #define kMDNSReplierInfoText_TXT \
1420 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1421 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1422 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1423 "\n" \
1424 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1425 "\n" \
1426 " 1. The first TXT record is defined as\n" \
1427 "\n" \
1428 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1429 " TYPE: TXT\n" \
1430 " CLASS: IN\n" \
1431 " TTL: 4500\n" \
1432 " RDLENGTH: L\n" \
1433 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1434 "\n" \
1435 " 2. For each i in [2, N], there is one TXT record:\n" \
1436 "\n" \
1437 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1438 " TYPE: TXT\n" \
1439 " CLASS: IN\n" \
1440 " TTL: 4500\n" \
1441 " RDLENGTH: L\n" \
1442 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1443 "\n" \
1444 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1445 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1446 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1447
1448 #define kMDNSReplierInfoText_A \
1449 "The replier has exactly N_max x N_a authoritative A records:\n" \
1450 "\n" \
1451 " 1. For each j in [1, N_a], an A record is defined as\n" \
1452 "\n" \
1453 " NAME: <hostname>.local.\n" \
1454 " TYPE: A\n" \
1455 " CLASS: IN\n" \
1456 " TTL: 120\n" \
1457 " RDLENGTH: 4\n" \
1458 " RDATA: 0.0.1.<j>\n" \
1459 "\n" \
1460 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1461 "\n" \
1462 " NAME: <hostname>-<i>.local.\n" \
1463 " TYPE: A\n" \
1464 " CLASS: IN\n" \
1465 " TTL: 120\n" \
1466 " RDLENGTH: 4\n" \
1467 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1468
1469 #define kMDNSReplierInfoText_AAAA \
1470 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1471 "\n" \
1472 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1473 "\n" \
1474 " NAME: <hostname>.local.\n" \
1475 " TYPE: AAAA\n" \
1476 " CLASS: IN\n" \
1477 " TTL: 120\n" \
1478 " RDLENGTH: 16\n" \
1479 " RDATA: 2001:db8:2::1:<j>\n" \
1480 "\n" \
1481 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1482 "\n" \
1483 " NAME: <hostname>-<i>.local.\n" \
1484 " TYPE: AAAA\n" \
1485 " CLASS: IN\n" \
1486 " TTL: 120\n" \
1487 " RDLENGTH: 16\n" \
1488 " RDATA: 2001:db8:2::<i>:<j>\n"
1489
1490 #define kMDNSReplierInfoText_Responses \
1491 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1492 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1493 "separate response messages.\n"
1494
1495 static const char * gMDNSReplier_Hostname = NULL;
1496 static const char * gMDNSReplier_ServiceTypeTag = NULL;
1497 static int gMDNSReplier_MaxInstanceCount = 1000;
1498 static int gMDNSReplier_NoAdditionals = false;
1499 static int gMDNSReplier_RecordCountA = 1;
1500 static int gMDNSReplier_RecordCountAAAA = 1;
1501 static double gMDNSReplier_UnicastDropRate = 0.0;
1502 static double gMDNSReplier_MulticastDropRate = 0.0;
1503 static int gMDNSReplier_MaxDropCount = 0;
1504 static int gMDNSReplier_UseIPv4 = false;
1505 static int gMDNSReplier_UseIPv6 = false;
1506 static int gMDNSReplier_Foreground = false;
1507 static const char * gMDNSReplier_FollowPID = NULL;
1508
1509 static CLIOption kMDNSReplierOpts[] =
1510 {
1511 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1512 StringOption( 'n', "hostname", &gMDNSReplier_Hostname, "string", "Base name to use for hostnames and service instance names.", true ),
1513 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1514 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount, "count", "Maximum number of service instances. (default: 1000)", false ),
1515 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals, "When answering queries, don't include any additional records." ),
1516 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1517 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1518 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1519 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1520 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1521 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4, "Use IPv4." ),
1522 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6, "Use IPv6." ),
1523 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground, "Direct log output to stdout instead of system logging." ),
1524 #if( TARGET_OS_DARWIN )
1525 StringOption( 0 , "follow", &gMDNSReplier_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1526 #endif
1527
1528 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro ),
1529 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters ),
1530 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR ),
1531 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV ),
1532 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT ),
1533 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A ),
1534 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA ),
1535 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses ),
1536 CLI_OPTION_END()
1537 };
1538
1539 static void MDNSReplierCmd( void );
1540
1541 //===========================================================================================================================
1542 // Test Command Options
1543 //===========================================================================================================================
1544
1545 #define kTestExitStatusSection_Name "Exit Status"
1546 #define kTestExitStatusSection_Text \
1547 "This test command can exit with one of three status codes:\n" \
1548 "\n" \
1549 "0 - The test ran to completion and passed.\n" \
1550 "1 - A fatal error prevented the test from completing.\n" \
1551 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1552 "\n" \
1553 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1554 "performance.\n"
1555
1556 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1557
1558 #define kGAIPerfTestSuiteName_Basic "basic"
1559 #define kGAIPerfTestSuiteName_Advanced "advanced"
1560
1561 static const char * gGAIPerf_TestSuite = NULL;
1562 static int gGAIPerf_CallDelayMs = 10;
1563 static int gGAIPerf_ServerDelayMs = 10;
1564 static int gGAIPerf_SkipPathEvalulation = false;
1565 static int gGAIPerf_BadUDPMode = false;
1566 static int gGAIPerf_IterationCount = 100;
1567 static int gGAIPerf_IterationTimeLimitMs = 100;
1568 static const char * gGAIPerf_OutputFilePath = NULL;
1569 static const char * gGAIPerf_OutputFormat = kOutputFormatStr_JSON;
1570 static int gGAIPerf_OutputAppendNewline = false;
1571
1572 static void GAIPerfCmd( void );
1573
1574 #define kGAIPerfSectionText_TestSuiteBasic \
1575 "This test suite consists of the following three test cases:\n" \
1576 "\n" \
1577 "Test Case #1: Resolve a domain name with\n" \
1578 "\n" \
1579 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1580 "\n" \
1581 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1582 "server queries.\n" \
1583 "\n" \
1584 "Test Case #2: Resolve a domain name with\n" \
1585 "\n" \
1586 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1587 "\n" \
1588 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1589 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1590 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1591 "\n" \
1592 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1593 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1594 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1595 "\n" \
1596 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1597
1598 #define kGAIPerfSectionText_TestSuiteAdvanced \
1599 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1600 "\n" \
1601 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1602 "\n" \
1603 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1604 "\n" \
1605 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1606 "server queries.\n" \
1607 "\n" \
1608 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1609 "\n" \
1610 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1611 "\n" \
1612 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1613 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1614 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1615 "\n" \
1616 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1617 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1618 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1619 "\n" \
1620 "N_c and N_a take on the following values, depending on the value of N:\n" \
1621 "\n" \
1622 " N_c is 0 if N is in [1, 8].\n" \
1623 " N_c is 1 if N is in [9, 16].\n" \
1624 " N_c is 2 if N is in [17, 24].\n" \
1625 " N_c is 4 if N is in [25, 32].\n" \
1626 "\n" \
1627 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1628 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1629 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1630 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1631 "\n" \
1632 "Finally,\n" \
1633 "\n" \
1634 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1635
1636 static CLIOption kGAIPerfOpts[] =
1637 {
1638 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1639 "\n"
1640 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1641 "\n"
1642 ),
1643 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1644 FormatOption( 'f', "format", &gGAIPerf_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1645 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1646 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount, "count", "The number of iterations per test case. (default: 100)", false ),
1647 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1648 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1649 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1650
1651 CLI_OPTION_GROUP( "DNS Server Options" ),
1652 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1653 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1654
1655 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic ),
1656 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced ),
1657 TestExitStatusSection(),
1658 CLI_OPTION_END()
1659 };
1660
1661 static void MDNSDiscoveryTestCmd( void );
1662
1663 static int gMDNSDiscoveryTest_InstanceCount = 100;
1664 static int gMDNSDiscoveryTest_TXTSize = 100;
1665 static int gMDNSDiscoveryTest_BrowseTimeSecs = 2;
1666 static int gMDNSDiscoveryTest_FlushCache = false;
1667 static char * gMDNSDiscoveryTest_Interface = NULL;
1668 static int gMDNSDiscoveryTest_NoAdditionals = false;
1669 static int gMDNSDiscoveryTest_RecordCountA = 1;
1670 static int gMDNSDiscoveryTest_RecordCountAAAA = 1;
1671 static double gMDNSDiscoveryTest_UnicastDropRate = 0.0;
1672 static double gMDNSDiscoveryTest_MulticastDropRate = 0.0;
1673 static int gMDNSDiscoveryTest_MaxDropCount = 0;
1674 static int gMDNSDiscoveryTest_UseIPv4 = false;
1675 static int gMDNSDiscoveryTest_UseIPv6 = false;
1676 static const char * gMDNSDiscoveryTest_OutputFormat = kOutputFormatStr_JSON;
1677 static int gMDNSDiscoveryTest_OutputAppendNewline = false;
1678 static const char * gMDNSDiscoveryTest_OutputFilePath = NULL;
1679
1680 static CLIOption kMDNSDiscoveryTestOpts[] =
1681 {
1682 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount, "count", "Number of service instances to discover. (default: 100)", false ),
1683 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1684 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1685 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1686
1687 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1688 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1689 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals, "When answering queries, don't include any additional records." ),
1690 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1691 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1692 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1693 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1694 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1695 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4, "Use IPv4." ),
1696 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6, "Use IPv6." ),
1697
1698 CLI_OPTION_GROUP( "Results" ),
1699 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1700 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1701
1702 TestExitStatusSection(),
1703 CLI_OPTION_END()
1704 };
1705
1706 static void DotLocalTestCmd( void );
1707
1708 static const char * gDotLocalTest_Interface = NULL;
1709 static const char * gDotLocalTest_OutputFormat = kOutputFormatStr_JSON;
1710 static const char * gDotLocalTest_OutputFilePath = NULL;
1711
1712 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1713 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1714 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1715 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1716 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1717 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1718 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1719
1720 #define kDotLocalTestSectionText_Description \
1721 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1722 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1723 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1724 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1725 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1726 "then the mdnsreplier's base hostname is test.local.\n" \
1727 "\n" \
1728 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1729 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1730 "\n" \
1731 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1732 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1733 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1734 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1735 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1736 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1737 "\n" \
1738 "Each subtest runs for five seconds.\n"
1739
1740 static CLIOption kDotLocalTestOpts[] =
1741 {
1742 StringOption( 'i', "interface", &gDotLocalTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1743
1744 CLI_OPTION_GROUP( "Results" ),
1745 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1746 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1747
1748 CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1749 TestExitStatusSection(),
1750 CLI_OPTION_END()
1751 };
1752
1753 static void ProbeConflictTestCmd( void );
1754
1755 static const char * gProbeConflictTest_Interface = NULL;
1756 static int gProbeConflictTest_UseComputerName = false;
1757 static const char * gProbeConflictTest_OutputFormat = kOutputFormatStr_JSON;
1758 static const char * gProbeConflictTest_OutputFilePath = NULL;
1759
1760 static CLIOption kProbeConflictTestOpts[] =
1761 {
1762 StringOption( 'i', "interface", &gProbeConflictTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1763 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1764
1765 CLI_OPTION_GROUP( "Results" ),
1766 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat, "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1767 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1768
1769 TestExitStatusSection(),
1770 CLI_OPTION_END()
1771 };
1772
1773 static void RegistrationTestCmd( void );
1774
1775 static int gRegistrationTest_BATSEnvironment = false;
1776 static const char * gRegistrationTest_OutputFormat = kOutputFormatStr_JSON;
1777 static const char * gRegistrationTest_OutputFilePath = NULL;
1778
1779 static CLIOption kRegistrationTestOpts[] =
1780 {
1781 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment, "Informs the test that it's running in a BATS environment.",
1782 "\n"
1783 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1784 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1785 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1786 "\n"
1787 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1788 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1789 ),
1790 CLI_OPTION_GROUP( "Results" ),
1791 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1792 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1793
1794 TestExitStatusSection(),
1795 CLI_OPTION_END()
1796 };
1797
1798 static void ExpensiveConstrainedTestCmd( void );
1799
1800 static const char * gExpensiveConstrainedTest_Interface = NULL;
1801 static const char * gExpensiveConstrainedTest_Name = NULL;
1802 static Boolean gExpensiveConstrainedTest_DenyExpensive = false;
1803 static Boolean gExpensiveConstrainedTest_DenyConstrained = false;
1804 static Boolean gExpensiveConstrainedTest_StartFromExpensive = false;
1805 static int gExpensiveConstrainedTest_ProtocolIPv4 = false;
1806 static int gExpensiveConstrainedTest_ProtocolIPv6 = false;
1807 static const char * gExpensiveConstrainedTest_OutputFormat = kOutputFormatStr_JSON;
1808 static const char * gExpensiveConstrainedTest_OutputFilePath = NULL;
1809
1810 static CLIOption kExpensiveConstrainedTestOpts[] =
1811 {
1812 CLI_OPTION_GROUP( "Results" ),
1813 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1814 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1815
1816 TestExitStatusSection(),
1817 CLI_OPTION_END()
1818 };
1819
1820 #if( MDNSRESPONDER_PROJECT )
1821 static void XCTestCmd( void );
1822
1823 static const char * gXCTest_Classname = NULL;
1824
1825 static CLIOption kXCTestOpts[] =
1826 {
1827 StringOption( 'c', "class", &gXCTest_Classname, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
1828 CLI_OPTION_END()
1829 };
1830 #endif
1831
1832 #if( TARGET_OS_DARWIN )
1833 static void KeepAliveTestCmd( void );
1834
1835 static const char * gKeepAliveTest_OutputFormat = kOutputFormatStr_JSON;
1836 static const char * gKeepAliveTest_OutputFilePath = NULL;
1837
1838 static CLIOption kKeepAliveTestOpts[] =
1839 {
1840 CLI_OPTION_GROUP( "Results" ),
1841 FormatOption( 'f', "format", &gKeepAliveTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1842 StringOption( 'o', "output", &gKeepAliveTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1843
1844 TestExitStatusSection(),
1845 CLI_OPTION_END()
1846 };
1847 #endif
1848
1849 static CLIOption kTestOpts[] =
1850 {
1851 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1852 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts, "Tests mDNS service discovery for correctness.", false ),
1853 Command( "dotlocal", DotLocalTestCmd, kDotLocalTestOpts, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1854 Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts, "Tests various probing conflict scenarios.", false ),
1855 Command( "registration", RegistrationTestCmd, kRegistrationTestOpts, "Tests service registrations.", false ),
1856 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd, kExpensiveConstrainedTestOpts, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1857 #if( MDNSRESPONDER_PROJECT )
1858 Command( "xctest", XCTestCmd, kXCTestOpts, "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
1859 #endif
1860 #if( TARGET_OS_DARWIN )
1861 Command( "keepalive", KeepAliveTestCmd, kKeepAliveTestOpts, "Tests keepalive record registrations.", false ),
1862 #endif
1863 CLI_OPTION_END()
1864 };
1865
1866 //===========================================================================================================================
1867 // SSDP Command Options
1868 //===========================================================================================================================
1869
1870 static int gSSDPDiscover_MX = 1;
1871 static const char * gSSDPDiscover_ST = "ssdp:all";
1872 static int gSSDPDiscover_ReceiveSecs = 1;
1873 static int gSSDPDiscover_UseIPv4 = false;
1874 static int gSSDPDiscover_UseIPv6 = false;
1875 static int gSSDPDiscover_Verbose = false;
1876
1877 static CLIOption kSSDPDiscoverOpts[] =
1878 {
1879 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1880 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1881 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1882 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1883 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1884 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1885 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
1886 CLI_OPTION_END()
1887 };
1888
1889 static void SSDPDiscoverCmd( void );
1890
1891 static CLIOption kSSDPOpts[] =
1892 {
1893 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
1894 CLI_OPTION_END()
1895 };
1896
1897 #if( TARGET_OS_DARWIN )
1898 //===========================================================================================================================
1899 // res_query Command Options
1900 //===========================================================================================================================
1901
1902 static void ResQueryCmd( void );
1903
1904 static const char * gResQuery_Name = NULL;
1905 static const char * gResQuery_Type = NULL;
1906 static const char * gResQuery_Class = NULL;
1907 static int gResQuery_UseLibInfo = false;
1908
1909 static CLIOption kResQueryOpts[] =
1910 {
1911 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
1912 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1913 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1914 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
1915 CLI_OPTION_END()
1916 };
1917
1918 //===========================================================================================================================
1919 // dns_query Command Options
1920 //===========================================================================================================================
1921
1922 static void ResolvDNSQueryCmd( void );
1923
1924 static const char * gResolvDNSQuery_Name = NULL;
1925 static const char * gResolvDNSQuery_Type = NULL;
1926 static const char * gResolvDNSQuery_Class = NULL;
1927 static const char * gResolvDNSQuery_Path = NULL;
1928
1929 static CLIOption kResolvDNSQueryOpts[] =
1930 {
1931 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
1932 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1933 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1934 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1935 CLI_OPTION_END()
1936 };
1937
1938 //===========================================================================================================================
1939 // CFHost Command Options
1940 //===========================================================================================================================
1941
1942 static void CFHostCmd( void );
1943
1944 static const char * gCFHost_Name = NULL;
1945 static int gCFHost_WaitSecs = 0;
1946
1947 static CLIOption kCFHostOpts[] =
1948 {
1949 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
1950 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1951 CLI_OPTION_END()
1952 };
1953
1954 static CLIOption kLegacyOpts[] =
1955 {
1956 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1957 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
1958 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
1959 CLI_OPTION_END()
1960 };
1961
1962 //===========================================================================================================================
1963 // DNSConfigAdd Command Options
1964 //===========================================================================================================================
1965
1966 static void DNSConfigAddCmd( void );
1967
1968 static CFStringRef gDNSConfigAdd_ID = NULL;
1969 static char ** gDNSConfigAdd_IPAddrArray = NULL;
1970 static size_t gDNSConfigAdd_IPAddrCount = 0;
1971 static char ** gDNSConfigAdd_DomainArray = NULL;
1972 static size_t gDNSConfigAdd_DomainCount = 0;
1973 static const char * gDNSConfigAdd_Interface = NULL;
1974
1975 static CLIOption kDNSConfigAddOpts[] =
1976 {
1977 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
1978 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1979 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1980 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
1981
1982 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1983 CLI_OPTION_END()
1984 };
1985
1986 //===========================================================================================================================
1987 // DNSConfigRemove Command Options
1988 //===========================================================================================================================
1989
1990 static void DNSConfigRemoveCmd( void );
1991
1992 static CFStringRef gDNSConfigRemove_ID = NULL;
1993
1994 static CLIOption kDNSConfigRemoveOpts[] =
1995 {
1996 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
1997
1998 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1999 CLI_OPTION_END()
2000 };
2001
2002 static CLIOption kDNSConfigOpts[] =
2003 {
2004 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2005 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2006 CLI_OPTION_END()
2007 };
2008
2009 //===========================================================================================================================
2010 // XPCSend
2011 //===========================================================================================================================
2012
2013 static void XPCSendCmd( void );
2014
2015 static const char * gXPCSend_ServiceName = NULL;
2016 static const char * gXPCSend_MessageStr = NULL;
2017
2018 static const char kXPCSendMessageSection_Name[] = "Message Argument";
2019 static const char kXPCSendMessageSection_Text[] =
2020 "XPC messages are described as a string using the following syntax.\n"
2021 "\n"
2022 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
2023 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
2024 "currently supported XPC types.\n"
2025 "\n"
2026 "Arrays begin with a '[' and end with a ']'.\n"
2027 "\n"
2028 "The following non-container XPC types are supported:\n"
2029 "\n"
2030 "Type Syntax Example\n"
2031 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
2032 "data data:<hex string> data:C0000201\n"
2033 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
2034 "string string:<string> string:hello\\ world\n"
2035 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
2036 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
2037 "\n"
2038 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
2039 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
2040
2041 static CLIOption kXPCSendOpts[] =
2042 {
2043 StringOption( 's', "service", &gXPCSend_ServiceName, "service name", "XPC service name.", true ),
2044 StringOption( 'm', "message", &gXPCSend_MessageStr, "message", "XPC message as a string.", true ),
2045
2046 CLI_SECTION( kXPCSendMessageSection_Name, kXPCSendMessageSection_Text ),
2047 CLI_OPTION_END()
2048 };
2049 #endif // TARGET_OS_DARWIN
2050
2051 #if( MDNSRESPONDER_PROJECT )
2052 //===========================================================================================================================
2053 // InterfaceMonitor
2054 //===========================================================================================================================
2055
2056 static void InterfaceMonitorCmd( void );
2057
2058 static CLIOption kInterfaceMonitorOpts[] =
2059 {
2060 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
2061 CLI_OPTION_END()
2062 };
2063
2064 //===========================================================================================================================
2065 // DNSProxy
2066 //===========================================================================================================================
2067
2068 static void DNSProxyCmd( void );
2069
2070 static char ** gDNSProxy_InputInterfaces = NULL;
2071 static size_t gDNSProxy_InputInterfaceCount = 0;
2072 static const char * gDNSProxy_OutputInterface = NULL;
2073
2074 static CLIOption kDNSProxyOpts[] =
2075 {
2076 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces, &gDNSProxy_InputInterfaceCount, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2077 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2078 CLI_OPTION_END()
2079 };
2080
2081 #endif // MDNSRESPONDER_PROJECT
2082
2083 //===========================================================================================================================
2084 // Command Table
2085 //===========================================================================================================================
2086
2087 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2088
2089 static void BrowseCmd( void );
2090 static void GetAddrInfoCmd( void );
2091 static void QueryRecordCmd( void );
2092 static void RegisterCmd( void );
2093 static void RegisterRecordCmd( void );
2094 static void ResolveCmd( void );
2095 static void ReconfirmCmd( void );
2096 static void GetAddrInfoPOSIXCmd( void );
2097 static void ReverseLookupCmd( void );
2098 static void PortMappingCmd( void );
2099 static void BrowseAllCmd( void );
2100 static void GetAddrInfoStressCmd( void );
2101 static void DNSQueryCmd( void );
2102 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2103 static void DNSCryptCmd( void );
2104 #endif
2105 static void MDNSQueryCmd( void );
2106 static void PIDToUUIDCmd( void );
2107 static void DaemonVersionCmd( void );
2108
2109 static CLIOption kGlobalOpts[] =
2110 {
2111 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2112 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2113 CLI_OPTION_HELP(),
2114
2115 // Common commands.
2116
2117 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2118 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2119 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2120 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2121 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2122 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2123 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2124 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2125 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2126 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2127 #if( TARGET_OS_DARWIN )
2128 Command( "registerKA", RegisterKACmd, kRegisterKA_Opts, "Uses DNSServiceSleepKeepalive_sockaddr() to register a keep alive record.", false ),
2129 #endif
2130 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2131
2132 // Uncommon commands.
2133
2134 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2135 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2136 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2137 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2138 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2139 #endif
2140 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2141 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2142 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2143 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2144 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2145 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2146 Command( "ssdp", NULL, kSSDPOpts, "Simple Service Discovery Protocol (SSDP).", true ),
2147 #if( TARGET_OS_DARWIN )
2148 Command( "legacy", NULL, kLegacyOpts, "Legacy DNS API.", true ),
2149 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2150 Command( "xpcsend", XPCSendCmd, kXPCSendOpts, "Sends a message to an XPC service.", true ),
2151 #endif
2152 #if( MDNSRESPONDER_PROJECT )
2153 Command( "interfaceMonitor", InterfaceMonitorCmd, kInterfaceMonitorOpts, "mDNSResponder's interface monitor.", true ),
2154 Command( "dnsproxy", DNSProxyCmd, kDNSProxyOpts, "Enables mDNSResponder's DNS proxy.", true ),
2155 #endif
2156 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2157
2158 CLI_COMMAND_HELP(),
2159 CLI_OPTION_END()
2160 };
2161
2162 //===========================================================================================================================
2163 // Helper Prototypes
2164 //===========================================================================================================================
2165
2166 #define kExitReason_OneShotDone "one-shot done"
2167 #define kExitReason_ReceivedResponse "received response"
2168 #define kExitReason_SIGINT "interrupt signal"
2169 #define kExitReason_Timeout "timeout"
2170 #define kExitReason_TimeLimit "time limit"
2171
2172 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2173
2174 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2175
2176 typedef enum
2177 {
2178 kConnectionType_None = 0,
2179 kConnectionType_Normal = 1,
2180 kConnectionType_DelegatePID = 2,
2181 kConnectionType_DelegateUUID = 3
2182
2183 } ConnectionType;
2184
2185 typedef struct
2186 {
2187 ConnectionType type;
2188 union
2189 {
2190 int32_t pid;
2191 uint8_t uuid[ 16 ];
2192
2193 } delegate;
2194
2195 } ConnectionDesc;
2196
2197 static OSStatus
2198 CreateConnectionFromArgString(
2199 const char * inString,
2200 dispatch_queue_t inQueue,
2201 DNSServiceRef * outSDRef,
2202 ConnectionDesc * outDesc );
2203 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2204 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2205 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2206 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2207
2208 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2209
2210 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2211 static const char * RecordTypeToString( unsigned int inValue );
2212
2213 static OSStatus
2214 DNSRecordDataToString(
2215 const void * inRDataPtr,
2216 size_t inRDataLen,
2217 unsigned int inRDataType,
2218 const void * inMsgPtr,
2219 size_t inMsgLen,
2220 char ** outString );
2221
2222 static OSStatus
2223 DNSMessageToText(
2224 const uint8_t * inMsgPtr,
2225 size_t inMsgLen,
2226 Boolean inIsMDNS,
2227 Boolean inPrintRaw,
2228 char ** outText );
2229
2230 static OSStatus
2231 WriteDNSQueryMessage(
2232 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2233 uint16_t inMsgID,
2234 uint16_t inFlags,
2235 const char * inQName,
2236 uint16_t inQType,
2237 uint16_t inQClass,
2238 size_t * outMsgLen );
2239
2240 // Dispatch helpers
2241
2242 typedef void ( *DispatchHandler )( void *inContext );
2243
2244 static OSStatus
2245 DispatchSignalSourceCreate(
2246 int inSignal,
2247 dispatch_queue_t inQueue,
2248 DispatchHandler inEventHandler,
2249 void * inContext,
2250 dispatch_source_t * outSource );
2251 static OSStatus
2252 DispatchSocketSourceCreate(
2253 SocketRef inSock,
2254 dispatch_source_type_t inType,
2255 dispatch_queue_t inQueue,
2256 DispatchHandler inEventHandler,
2257 DispatchHandler inCancelHandler,
2258 void * inContext,
2259 dispatch_source_t * outSource );
2260
2261 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2262 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2263
2264 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2265 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2266
2267 static OSStatus
2268 DispatchTimerCreate(
2269 dispatch_time_t inStart,
2270 uint64_t inIntervalNs,
2271 uint64_t inLeewayNs,
2272 dispatch_queue_t inQueue,
2273 DispatchHandler inEventHandler,
2274 DispatchHandler inCancelHandler,
2275 void * inContext,
2276 dispatch_source_t * outTimer );
2277
2278 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2279 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2280
2281 static OSStatus
2282 DispatchProcessMonitorCreate(
2283 pid_t inPID,
2284 unsigned long inFlags,
2285 dispatch_queue_t inQueue,
2286 DispatchHandler inEventHandler,
2287 DispatchHandler inCancelHandler,
2288 void * inContext,
2289 dispatch_source_t * outMonitor );
2290
2291 static const char * ServiceTypeDescription( const char *inName );
2292
2293 typedef struct
2294 {
2295 SocketRef sock; // Socket.
2296 void * userContext; // User context.
2297 int32_t refCount; // Reference count.
2298
2299 } SocketContext;
2300
2301 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
2302 static SocketContext * SocketContextRetain( SocketContext *inContext );
2303 static void SocketContextRelease( SocketContext *inContext );
2304 static void SocketContextCancelHandler( void *inContext );
2305
2306 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2307
2308 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2309 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2310 #if( TARGET_OS_DARWIN )
2311 static int64_t _StringToInt64( const char *inString, OSStatus *outError );
2312 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError );
2313 static pid_t _StringToPID( const char *inString, OSStatus *outError );
2314 static OSStatus
2315 _ParseEscapedString(
2316 const char * inSrc,
2317 const char * inEnd,
2318 const char * inDelimiters,
2319 char * inBufPtr,
2320 size_t inBufLen,
2321 size_t * outCopiedLen,
2322 size_t * outActualLen,
2323 const char ** outPtr );
2324 #endif
2325 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2326 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2327 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2328 #if( TARGET_OS_DARWIN )
2329 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2330 #endif
2331 static OSStatus
2332 _ServerSocketOpenEx2(
2333 int inFamily,
2334 int inType,
2335 int inProtocol,
2336 const void * inAddr,
2337 int inPort,
2338 int * outPort,
2339 int inRcvBufSize,
2340 Boolean inNoPortReuse,
2341 SocketRef * outSock );
2342
2343 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2344 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2345
2346 static OSStatus
2347 CreateMulticastSocket(
2348 const struct sockaddr * inAddr,
2349 int inPort,
2350 const char * inIfName,
2351 uint32_t inIfIndex,
2352 Boolean inJoin,
2353 int * outPort,
2354 SocketRef * outSock );
2355
2356 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2357 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2358 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2359 static OSStatus CheckRootUser( void );
2360 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... );
2361 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2362 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2363 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2364 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2365 static OSStatus
2366 CreateNSECRecordData(
2367 const uint8_t * inNextDomainName,
2368 uint8_t ** outPtr,
2369 size_t * outLen,
2370 unsigned int inTypeCount,
2371 ... );
2372 static OSStatus
2373 AppendSOARecord(
2374 DataBuffer * inDB,
2375 const uint8_t * inNamePtr,
2376 size_t inNameLen,
2377 uint16_t inType,
2378 uint16_t inClass,
2379 uint32_t inTTL,
2380 const uint8_t * inMName,
2381 const uint8_t * inRName,
2382 uint32_t inSerial,
2383 uint32_t inRefresh,
2384 uint32_t inRetry,
2385 uint32_t inExpire,
2386 uint32_t inMinimumTTL,
2387 size_t * outLen );
2388 static OSStatus
2389 CreateSOARecordData(
2390 const uint8_t * inMName,
2391 const uint8_t * inRName,
2392 uint32_t inSerial,
2393 uint32_t inRefresh,
2394 uint32_t inRetry,
2395 uint32_t inExpire,
2396 uint32_t inMinimumTTL,
2397 uint8_t ** outPtr,
2398 size_t * outLen );
2399 static OSStatus
2400 _DataBuffer_AppendDNSQuestion(
2401 DataBuffer * inDB,
2402 const uint8_t * inNamePtr,
2403 size_t inNameLen,
2404 uint16_t inType,
2405 uint16_t inClass );
2406 static OSStatus
2407 _DataBuffer_AppendDNSRecord(
2408 DataBuffer * inDB,
2409 const uint8_t * inNamePtr,
2410 size_t inNameLen,
2411 uint16_t inType,
2412 uint16_t inClass,
2413 uint32_t inTTL,
2414 const uint8_t * inRDataPtr,
2415 size_t inRDataLen );
2416 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2417
2418 typedef struct MDNSInterfaceItem MDNSInterfaceItem;
2419 struct MDNSInterfaceItem
2420 {
2421 MDNSInterfaceItem * next;
2422 char * ifName;
2423 uint32_t ifIndex;
2424 Boolean hasIPv4;
2425 Boolean hasIPv6;
2426 Boolean isAWDL;
2427 Boolean isWiFi;
2428 };
2429
2430 typedef enum
2431 {
2432 kMDNSInterfaceSubset_All = 0, // All mDNS-capable interfaces.
2433 kMDNSInterfaceSubset_AWDL = 1, // All mDNS-capable AWDL interfaces.
2434 kMDNSInterfaceSubset_NonAWDL = 2 // All mDNS-capable non-AWDL iterfaces.
2435
2436 } MDNSInterfaceSubset;
2437
2438 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2439 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2440 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2441 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2442
2443 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2444 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName );
2445 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName );
2446 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2447
2448 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2449 static OSStatus
2450 _StringToIPv4Address(
2451 const char * inStr,
2452 StringToIPAddressFlags inFlags,
2453 uint32_t * outIP,
2454 int * outPort,
2455 uint32_t * outSubnet,
2456 uint32_t * outRouter,
2457 const char ** outStr );
2458 static void _StringArray_Free( char **inArray, size_t inCount );
2459 static OSStatus
2460 _StringToIPv6Address(
2461 const char * inStr,
2462 StringToIPAddressFlags inFlags,
2463 uint8_t outIPv6[ 16 ],
2464 uint32_t * outScope,
2465 int * outPort,
2466 int * outPrefix,
2467 const char ** outStr );
2468 static Boolean
2469 _ParseQuotedEscapedString(
2470 const char * inSrc,
2471 const char * inEnd,
2472 const char * inDelimiters,
2473 char * inBuf,
2474 size_t inMaxLen,
2475 size_t * outCopiedLen,
2476 size_t * outTotalLen,
2477 const char ** outSrc );
2478 static void * _memdup( const void *inPtr, size_t inLen );
2479 static int _memicmp( const void *inP1, const void *inP2, size_t inLen );
2480 static uint32_t _FNV1( const void *inData, size_t inSize );
2481
2482 #define Unused( X ) (void)(X)
2483
2484 //===========================================================================================================================
2485 // MDNSCollider
2486 //===========================================================================================================================
2487
2488 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2489
2490 typedef uint32_t MDNSColliderProtocols;
2491 #define kMDNSColliderProtocol_None 0
2492 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2493 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2494
2495 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2496
2497 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2498 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2499 static void MDNSColliderStop( MDNSColliderRef inCollider );
2500 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2501 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2502 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2503 static void
2504 MDNSColliderSetStopHandler(
2505 MDNSColliderRef inCollider,
2506 MDNSColliderStopHandler_f inStopHandler,
2507 void * inStopContext );
2508 static OSStatus
2509 MDNSColliderSetRecord(
2510 MDNSColliderRef inCollider,
2511 const uint8_t * inName,
2512 uint16_t inType,
2513 const void * inRDataPtr,
2514 size_t inRDataLen );
2515 static CFTypeID MDNSColliderGetTypeID( void );
2516
2517 //===========================================================================================================================
2518 // ServiceBrowser
2519 //===========================================================================================================================
2520
2521 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2522 typedef struct ServiceBrowserResults ServiceBrowserResults;
2523 typedef struct SBRDomain SBRDomain;
2524 typedef struct SBRServiceType SBRServiceType;
2525 typedef struct SBRServiceInstance SBRServiceInstance;
2526 typedef struct SBRIPAddress SBRIPAddress;
2527
2528 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2529
2530 struct ServiceBrowserResults
2531 {
2532 SBRDomain * domainList; // List of domains in which services were found.
2533 };
2534
2535 struct SBRDomain
2536 {
2537 SBRDomain * next; // Next domain in list.
2538 char * name; // Name of domain represented by this object.
2539 SBRServiceType * typeList; // List of service types in this domain.
2540 };
2541
2542 struct SBRServiceType
2543 {
2544 SBRServiceType * next; // Next service type in list.
2545 char * name; // Name of service type represented by this object.
2546 SBRServiceInstance * instanceList; // List of service instances of this service type.
2547 };
2548
2549 struct SBRServiceInstance
2550 {
2551 SBRServiceInstance * next; // Next service instance in list.
2552 char * name; // Name of service instance represented by this object.
2553 char * hostname; // Target from service instance's SRV record.
2554 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2555 uint16_t port; // Port from service instance's SRV record.
2556 uint8_t * txtPtr; // Service instance's TXT record data.
2557 size_t txtLen; // Service instance's TXT record data length.
2558 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2559 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2560 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2561 };
2562
2563 struct SBRIPAddress
2564 {
2565 SBRIPAddress * next; // Next IP address in list.
2566 sockaddr_ip sip; // IPv4 or IPv6 address.
2567 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2568 };
2569
2570 static CFTypeID ServiceBrowserGetTypeID( void );
2571 static OSStatus
2572 ServiceBrowserCreate(
2573 dispatch_queue_t inQueue,
2574 uint32_t inInterfaceIndex,
2575 const char * inDomain,
2576 unsigned int inBrowseTimeSecs,
2577 Boolean inIncludeAWDL,
2578 ServiceBrowserRef * outBrowser );
2579 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2580 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2581 static void
2582 ServiceBrowserSetCallback(
2583 ServiceBrowserRef inBrowser,
2584 ServiceBrowserCallback_f inCallback,
2585 void * inContext );
2586 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2587 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2588
2589 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2590
2591 //===========================================================================================================================
2592 // main
2593 //===========================================================================================================================
2594
2595 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2596 static int \
2597 _PrintFExtension ## NAME ## Handler( \
2598 PrintFContext * inContext, \
2599 PrintFFormat * inFormat, \
2600 PrintFVAList * inArgs, \
2601 void * inUserContext )
2602
2603 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
2604 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
2605 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
2606 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
2607
2608 int main( int argc, const char **argv )
2609 {
2610 OSStatus err;
2611
2612 // Route DebugServices logging output to stderr.
2613
2614 dlog_control( "DebugServices:output=file;stderr" );
2615
2616 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler, NULL );
2617 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler, NULL );
2618 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler, NULL );
2619 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler, NULL );
2620 CLIInit( argc, argv );
2621 err = CLIParse( kGlobalOpts, kCLIFlags_None );
2622 if( err ) exit( 1 );
2623
2624 return( gExitCode );
2625 }
2626
2627 //===========================================================================================================================
2628 // VersionOptionCallback
2629 //===========================================================================================================================
2630
2631 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
2632 {
2633 const char * srcVers;
2634 #if( MDNSRESPONDER_PROJECT )
2635 char srcStr[ 16 ];
2636 #endif
2637
2638 Unused( inOption );
2639 Unused( inArg );
2640 Unused( inUnset );
2641
2642 #if( MDNSRESPONDER_PROJECT )
2643 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
2644 #else
2645 srcVers = DNSSDUTIL_SOURCE_VERSION;
2646 #endif
2647 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
2648
2649 return( kEndingErr );
2650 }
2651
2652 //===========================================================================================================================
2653 // BrowseCmd
2654 //===========================================================================================================================
2655
2656 typedef struct BrowseResolveOp BrowseResolveOp;
2657
2658 struct BrowseResolveOp
2659 {
2660 BrowseResolveOp * next; // Next resolve operation in list.
2661 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2662 char * fullName; // Full name of the service to resolve.
2663 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2664 };
2665
2666 typedef struct
2667 {
2668 DNSServiceRef mainRef; // Main sdRef for shared connection.
2669 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
2670 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
2671 const char * domain; // Domain for DNSServiceBrowse operation(s).
2672 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
2673 char ** serviceTypes; // Array of service types to browse for.
2674 size_t serviceTypesCount; // Count of array of service types to browse for.
2675 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
2676 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
2677 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
2678 Boolean printedHeader; // True if results header has been printed.
2679 Boolean doResolve; // True if service instances are to be resolved.
2680 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
2681
2682 } BrowseContext;
2683
2684 static void BrowsePrintPrologue( const BrowseContext *inContext );
2685 static void BrowseContextFree( BrowseContext *inContext );
2686 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
2687 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
2688 static void DNSSD_API
2689 BrowseCallback(
2690 DNSServiceRef inSDRef,
2691 DNSServiceFlags inFlags,
2692 uint32_t inInterfaceIndex,
2693 DNSServiceErrorType inError,
2694 const char * inName,
2695 const char * inRegType,
2696 const char * inDomain,
2697 void * inContext );
2698 static void DNSSD_API
2699 BrowseResolveCallback(
2700 DNSServiceRef inSDRef,
2701 DNSServiceFlags inFlags,
2702 uint32_t inInterfaceIndex,
2703 DNSServiceErrorType inError,
2704 const char * inFullName,
2705 const char * inHostname,
2706 uint16_t inPort,
2707 uint16_t inTXTLen,
2708 const unsigned char * inTXTPtr,
2709 void * inContext );
2710 static void DNSSD_API
2711 BrowseQueryRecordCallback(
2712 DNSServiceRef inSDRef,
2713 DNSServiceFlags inFlags,
2714 uint32_t inInterfaceIndex,
2715 DNSServiceErrorType inError,
2716 const char * inFullName,
2717 uint16_t inType,
2718 uint16_t inClass,
2719 uint16_t inRDataLen,
2720 const void * inRDataPtr,
2721 uint32_t inTTL,
2722 void * inContext );
2723
2724 static void BrowseCmd( void )
2725 {
2726 OSStatus err;
2727 size_t i;
2728 BrowseContext * context = NULL;
2729 dispatch_source_t signalSource = NULL;
2730 int useMainConnection;
2731
2732 // Set up SIGINT handler.
2733
2734 signal( SIGINT, SIG_IGN );
2735 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
2736 require_noerr( err, exit );
2737 dispatch_resume( signalSource );
2738
2739 // Create context.
2740
2741 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
2742 require_action( context, exit, err = kNoMemoryErr );
2743
2744 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
2745 require_action( context->opRefs, exit, err = kNoMemoryErr );
2746 context->opRefsCount = gBrowse_ServiceTypesCount;
2747
2748 // Check command parameters.
2749
2750 if( gBrowse_TimeLimitSecs < 0 )
2751 {
2752 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
2753 err = kParamErr;
2754 goto exit;
2755 }
2756
2757 // Create main connection.
2758
2759 if( gConnectionOpt )
2760 {
2761 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2762 require_noerr_quiet( err, exit );
2763 useMainConnection = true;
2764 }
2765 else
2766 {
2767 useMainConnection = false;
2768 }
2769
2770 // Get flags.
2771
2772 context->flags = GetDNSSDFlagsFromOpts();
2773 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2774
2775 // Get interface.
2776
2777 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2778 require_noerr_quiet( err, exit );
2779
2780 // Set remaining parameters.
2781
2782 context->serviceTypes = gBrowse_ServiceTypes;
2783 context->serviceTypesCount = gBrowse_ServiceTypesCount;
2784 context->domain = gBrowse_Domain;
2785 context->doResolve = gBrowse_DoResolve ? true : false;
2786 context->timeLimitSecs = gBrowse_TimeLimitSecs;
2787 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
2788
2789 // Print prologue.
2790
2791 BrowsePrintPrologue( context );
2792
2793 // Start operation(s).
2794
2795 for( i = 0; i < context->serviceTypesCount; ++i )
2796 {
2797 DNSServiceRef sdRef;
2798
2799 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2800 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
2801 BrowseCallback, context );
2802 require_noerr( err, exit );
2803
2804 context->opRefs[ i ] = sdRef;
2805 if( !useMainConnection )
2806 {
2807 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
2808 require_noerr( err, exit );
2809 }
2810 }
2811
2812 // Set time limit.
2813
2814 if( context->timeLimitSecs > 0 )
2815 {
2816 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2817 kExitReason_TimeLimit, Exit );
2818 }
2819 dispatch_main();
2820
2821 exit:
2822 dispatch_source_forget( &signalSource );
2823 if( context ) BrowseContextFree( context );
2824 if( err ) exit( 1 );
2825 }
2826
2827 //===========================================================================================================================
2828 // BrowsePrintPrologue
2829 //===========================================================================================================================
2830
2831 static void BrowsePrintPrologue( const BrowseContext *inContext )
2832 {
2833 const int timeLimitSecs = inContext->timeLimitSecs;
2834 const char * const * ptr = (const char **) inContext->serviceTypes;
2835 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
2836 char ifName[ kInterfaceNameBufLen ];
2837
2838 InterfaceIndexToName( inContext->ifIndex, ifName );
2839
2840 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2841 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2842 FPrintF( stdout, "Service types: %s", *ptr++ );
2843 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
2844 FPrintF( stdout, "\n" );
2845 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2846 FPrintF( stdout, "Time limit: " );
2847 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2848 else FPrintF( stdout, "∞\n" );
2849 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2850 FPrintF( stdout, "---\n" );
2851 }
2852
2853 //===========================================================================================================================
2854 // BrowseContextFree
2855 //===========================================================================================================================
2856
2857 static void BrowseContextFree( BrowseContext *inContext )
2858 {
2859 size_t i;
2860
2861 for( i = 0; i < inContext->opRefsCount; ++i )
2862 {
2863 DNSServiceForget( &inContext->opRefs[ i ] );
2864 }
2865 if( inContext->serviceTypes )
2866 {
2867 _StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
2868 inContext->serviceTypes = NULL;
2869 inContext->serviceTypesCount = 0;
2870 }
2871 DNSServiceForget( &inContext->mainRef );
2872 free( inContext );
2873 }
2874
2875 //===========================================================================================================================
2876 // BrowseResolveOpCreate
2877 //===========================================================================================================================
2878
2879 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
2880 {
2881 OSStatus err;
2882 BrowseResolveOp * resolveOp;
2883
2884 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
2885 require_action( resolveOp, exit, err = kNoMemoryErr );
2886
2887 resolveOp->fullName = strdup( inFullName );
2888 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
2889
2890 resolveOp->interfaceIndex = inInterfaceIndex;
2891
2892 *outOp = resolveOp;
2893 resolveOp = NULL;
2894 err = kNoErr;
2895
2896 exit:
2897 if( resolveOp ) BrowseResolveOpFree( resolveOp );
2898 return( err );
2899 }
2900
2901 //===========================================================================================================================
2902 // BrowseResolveOpFree
2903 //===========================================================================================================================
2904
2905 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
2906 {
2907 DNSServiceForget( &inOp->sdRef );
2908 ForgetMem( &inOp->fullName );
2909 free( inOp );
2910 }
2911
2912 //===========================================================================================================================
2913 // BrowseCallback
2914 //===========================================================================================================================
2915
2916 static void DNSSD_API
2917 BrowseCallback(
2918 DNSServiceRef inSDRef,
2919 DNSServiceFlags inFlags,
2920 uint32_t inInterfaceIndex,
2921 DNSServiceErrorType inError,
2922 const char * inName,
2923 const char * inRegType,
2924 const char * inDomain,
2925 void * inContext )
2926 {
2927 BrowseContext * const context = (BrowseContext *) inContext;
2928 OSStatus err;
2929 BrowseResolveOp * newOp = NULL;
2930 BrowseResolveOp ** p;
2931 char fullName[ kDNSServiceMaxDomainName ];
2932 struct timeval now;
2933
2934 Unused( inSDRef );
2935
2936 gettimeofday( &now, NULL );
2937
2938 err = inError;
2939 require_noerr( err, exit );
2940
2941 if( !context->printedHeader )
2942 {
2943 FPrintF( stdout, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2944 context->printedHeader = true;
2945 }
2946 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2947 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2948
2949 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2950
2951 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2952 require_noerr( err, exit );
2953
2954 if( inFlags & kDNSServiceFlagsAdd )
2955 {
2956 DNSServiceRef sdRef;
2957 DNSServiceFlags flags;
2958
2959 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2960 require_noerr( err, exit );
2961
2962 if( context->mainRef )
2963 {
2964 sdRef = context->mainRef;
2965 flags = kDNSServiceFlagsShareConnection;
2966 }
2967 else
2968 {
2969 flags = 0;
2970 }
2971 if( context->doResolve )
2972 {
2973 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2974 NULL );
2975 require_noerr( err, exit );
2976 }
2977 else
2978 {
2979 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2980 BrowseQueryRecordCallback, NULL );
2981 require_noerr( err, exit );
2982 }
2983
2984 newOp->sdRef = sdRef;
2985 if( !context->mainRef )
2986 {
2987 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2988 require_noerr( err, exit );
2989 }
2990 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2991 *p = newOp;
2992 newOp = NULL;
2993 }
2994 else
2995 {
2996 BrowseResolveOp * resolveOp;
2997
2998 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2999 {
3000 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
3001 {
3002 break;
3003 }
3004 }
3005 if( resolveOp )
3006 {
3007 *p = resolveOp->next;
3008 BrowseResolveOpFree( resolveOp );
3009 }
3010 }
3011
3012 exit:
3013 if( newOp ) BrowseResolveOpFree( newOp );
3014 if( err ) exit( 1 );
3015 }
3016
3017 //===========================================================================================================================
3018 // BrowseQueryRecordCallback
3019 //===========================================================================================================================
3020
3021 static void DNSSD_API
3022 BrowseQueryRecordCallback(
3023 DNSServiceRef inSDRef,
3024 DNSServiceFlags inFlags,
3025 uint32_t inInterfaceIndex,
3026 DNSServiceErrorType inError,
3027 const char * inFullName,
3028 uint16_t inType,
3029 uint16_t inClass,
3030 uint16_t inRDataLen,
3031 const void * inRDataPtr,
3032 uint32_t inTTL,
3033 void * inContext )
3034 {
3035 OSStatus err;
3036 struct timeval now;
3037
3038 Unused( inSDRef );
3039 Unused( inClass );
3040 Unused( inTTL );
3041 Unused( inContext );
3042
3043 gettimeofday( &now, NULL );
3044
3045 err = inError;
3046 require_noerr( err, exit );
3047 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
3048
3049 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
3050 &now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
3051 (size_t) inRDataLen );
3052
3053 exit:
3054 if( err ) exit( 1 );
3055 }
3056
3057 //===========================================================================================================================
3058 // BrowseResolveCallback
3059 //===========================================================================================================================
3060
3061 static void DNSSD_API
3062 BrowseResolveCallback(
3063 DNSServiceRef inSDRef,
3064 DNSServiceFlags inFlags,
3065 uint32_t inInterfaceIndex,
3066 DNSServiceErrorType inError,
3067 const char * inFullName,
3068 const char * inHostname,
3069 uint16_t inPort,
3070 uint16_t inTXTLen,
3071 const unsigned char * inTXTPtr,
3072 void * inContext )
3073 {
3074 struct timeval now;
3075 char errorStr[ 64 ];
3076
3077 Unused( inSDRef );
3078 Unused( inFlags );
3079 Unused( inContext );
3080
3081 gettimeofday( &now, NULL );
3082
3083 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3084
3085 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3086 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3087 if( inTXTLen == 1 )
3088 {
3089 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3090 }
3091 else
3092 {
3093 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3094 }
3095 }
3096
3097 //===========================================================================================================================
3098 // GetAddrInfoCmd
3099 //===========================================================================================================================
3100
3101 typedef struct
3102 {
3103 DNSServiceRef mainRef; // Main sdRef for shared connection.
3104 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3105 const char * name; // Hostname to resolve.
3106 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3107 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3108 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3109 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3110 Boolean printedHeader; // True if the results header has been printed.
3111 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3112 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3113 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3114
3115 } GetAddrInfoContext;
3116
3117 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3118 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3119 static void DNSSD_API
3120 GetAddrInfoCallback(
3121 DNSServiceRef inSDRef,
3122 DNSServiceFlags inFlags,
3123 uint32_t inInterfaceIndex,
3124 DNSServiceErrorType inError,
3125 const char * inHostname,
3126 const struct sockaddr * inSockAddr,
3127 uint32_t inTTL,
3128 void * inContext );
3129
3130 static void GetAddrInfoCmd( void )
3131 {
3132 OSStatus err;
3133 DNSServiceRef sdRef;
3134 GetAddrInfoContext * context = NULL;
3135 dispatch_source_t signalSource = NULL;
3136 int useMainConnection;
3137
3138 // Set up SIGINT handler.
3139
3140 signal( SIGINT, SIG_IGN );
3141 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3142 require_noerr( err, exit );
3143 dispatch_resume( signalSource );
3144
3145 // Check command parameters.
3146
3147 if( gGetAddrInfo_TimeLimitSecs < 0 )
3148 {
3149 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3150 err = kParamErr;
3151 goto exit;
3152 }
3153
3154 // Create context.
3155
3156 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3157 require_action( context, exit, err = kNoMemoryErr );
3158
3159 // Create main connection.
3160
3161 if( gConnectionOpt )
3162 {
3163 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3164 require_noerr_quiet( err, exit );
3165 useMainConnection = true;
3166 }
3167 else
3168 {
3169 useMainConnection = false;
3170 }
3171
3172 // Get flags.
3173
3174 context->flags = GetDNSSDFlagsFromOpts();
3175 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3176
3177 // Get interface.
3178
3179 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3180 require_noerr_quiet( err, exit );
3181
3182 // Set remaining parameters.
3183
3184 context->name = gGetAddrInfo_Name;
3185 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3186 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3187 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3188 if( gGetAddrInfo_OneShot )
3189 {
3190 context->oneShotMode = true;
3191 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3192 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3193 }
3194
3195 // Print prologue.
3196
3197 GetAddrInfoPrintPrologue( context );
3198
3199 // Start operation.
3200
3201 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3202 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3203 GetAddrInfoCallback, context );
3204 require_noerr( err, exit );
3205
3206 context->opRef = sdRef;
3207 if( !useMainConnection )
3208 {
3209 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3210 require_noerr( err, exit );
3211 }
3212
3213 // Set time limit.
3214
3215 if( context->timeLimitSecs > 0 )
3216 {
3217 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3218 kExitReason_TimeLimit, Exit );
3219 }
3220 dispatch_main();
3221
3222 exit:
3223 dispatch_source_forget( &signalSource );
3224 if( context ) GetAddrInfoContextFree( context );
3225 if( err ) exit( 1 );
3226 }
3227
3228 //===========================================================================================================================
3229 // GetAddrInfoPrintPrologue
3230 //===========================================================================================================================
3231
3232 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3233 {
3234 const int timeLimitSecs = inContext->timeLimitSecs;
3235 char ifName[ kInterfaceNameBufLen ];
3236
3237 InterfaceIndexToName( inContext->ifIndex, ifName );
3238
3239 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3240 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3241 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3242 FPrintF( stdout, "Name: %s\n", inContext->name );
3243 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3244 FPrintF( stdout, "Time limit: " );
3245 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3246 else FPrintF( stdout, "∞\n" );
3247 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3248 FPrintF( stdout, "---\n" );
3249 }
3250
3251 //===========================================================================================================================
3252 // GetAddrInfoContextFree
3253 //===========================================================================================================================
3254
3255 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3256 {
3257 DNSServiceForget( &inContext->opRef );
3258 DNSServiceForget( &inContext->mainRef );
3259 free( inContext );
3260 }
3261
3262 //===========================================================================================================================
3263 // GetAddrInfoCallback
3264 //===========================================================================================================================
3265
3266 static void DNSSD_API
3267 GetAddrInfoCallback(
3268 DNSServiceRef inSDRef,
3269 DNSServiceFlags inFlags,
3270 uint32_t inInterfaceIndex,
3271 DNSServiceErrorType inError,
3272 const char * inHostname,
3273 const struct sockaddr * inSockAddr,
3274 uint32_t inTTL,
3275 void * inContext )
3276 {
3277 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3278 struct timeval now;
3279 OSStatus err;
3280 const char * addrStr;
3281 char addrStrBuf[ kSockAddrStringMaxSize ];
3282
3283 Unused( inSDRef );
3284
3285 gettimeofday( &now, NULL );
3286
3287 switch( inError )
3288 {
3289 case kDNSServiceErr_NoError:
3290 case kDNSServiceErr_NoSuchRecord:
3291 err = kNoErr;
3292 break;
3293
3294 case kDNSServiceErr_Timeout:
3295 Exit( kExitReason_Timeout );
3296
3297 default:
3298 err = inError;
3299 goto exit;
3300 }
3301
3302 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3303 {
3304 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3305 err = kTypeErr;
3306 goto exit;
3307 }
3308
3309 if( !inError )
3310 {
3311 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3312 require_noerr( err, exit );
3313 addrStr = addrStrBuf;
3314 }
3315 else
3316 {
3317 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3318 }
3319
3320 if( !context->printedHeader )
3321 {
3322 FPrintF( stdout, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3323 context->printedHeader = true;
3324 }
3325 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3326 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3327
3328 if( context->oneShotMode )
3329 {
3330 if( inFlags & kDNSServiceFlagsAdd )
3331 {
3332 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3333 else context->needIPv6 = false;
3334 }
3335 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3336 {
3337 Exit( kExitReason_OneShotDone );
3338 }
3339 }
3340
3341 exit:
3342 if( err ) exit( 1 );
3343 }
3344
3345 //===========================================================================================================================
3346 // QueryRecordCmd
3347 //===========================================================================================================================
3348
3349 typedef struct
3350 {
3351 DNSServiceRef mainRef; // Main sdRef for shared connection.
3352 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3353 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3354 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3355 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3356 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3357 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3358 Boolean printedHeader; // True if the results header was printed.
3359 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3360 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3361 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3362
3363 } QueryRecordContext;
3364
3365 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3366 static void QueryRecordContextFree( QueryRecordContext *inContext );
3367 static void DNSSD_API
3368 QueryRecordCallback(
3369 DNSServiceRef inSDRef,
3370 DNSServiceFlags inFlags,
3371 uint32_t inInterfaceIndex,
3372 DNSServiceErrorType inError,
3373 const char * inFullName,
3374 uint16_t inType,
3375 uint16_t inClass,
3376 uint16_t inRDataLen,
3377 const void * inRDataPtr,
3378 uint32_t inTTL,
3379 void * inContext );
3380
3381 static void QueryRecordCmd( void )
3382 {
3383 OSStatus err;
3384 DNSServiceRef sdRef;
3385 QueryRecordContext * context = NULL;
3386 dispatch_source_t signalSource = NULL;
3387 int useMainConnection;
3388
3389 // Set up SIGINT handler.
3390
3391 signal( SIGINT, SIG_IGN );
3392 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3393 require_noerr( err, exit );
3394 dispatch_resume( signalSource );
3395
3396 // Create context.
3397
3398 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3399 require_action( context, exit, err = kNoMemoryErr );
3400
3401 // Check command parameters.
3402
3403 if( gQueryRecord_TimeLimitSecs < 0 )
3404 {
3405 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3406 err = kParamErr;
3407 goto exit;
3408 }
3409
3410 // Create main connection.
3411
3412 if( gConnectionOpt )
3413 {
3414 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3415 require_noerr_quiet( err, exit );
3416 useMainConnection = true;
3417 }
3418 else
3419 {
3420 useMainConnection = false;
3421 }
3422
3423 // Get flags.
3424
3425 context->flags = GetDNSSDFlagsFromOpts();
3426 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3427
3428 // Get interface.
3429
3430 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3431 require_noerr_quiet( err, exit );
3432
3433 // Get record type.
3434
3435 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3436 require_noerr( err, exit );
3437
3438 // Set remaining parameters.
3439
3440 context->recordName = gQueryRecord_Name;
3441 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3442 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3443 context->printRawRData = gQueryRecord_RawRData ? true : false;
3444
3445 // Print prologue.
3446
3447 QueryRecordPrintPrologue( context );
3448
3449 // Start operation.
3450
3451 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3452 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3453 kDNSServiceClass_IN, QueryRecordCallback, context );
3454 require_noerr( err, exit );
3455
3456 context->opRef = sdRef;
3457 if( !useMainConnection )
3458 {
3459 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3460 require_noerr( err, exit );
3461 }
3462
3463 // Set time limit.
3464
3465 if( context->timeLimitSecs > 0 )
3466 {
3467 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3468 Exit );
3469 }
3470 dispatch_main();
3471
3472 exit:
3473 dispatch_source_forget( &signalSource );
3474 if( context ) QueryRecordContextFree( context );
3475 if( err ) exit( 1 );
3476 }
3477
3478 //===========================================================================================================================
3479 // QueryRecordContextFree
3480 //===========================================================================================================================
3481
3482 static void QueryRecordContextFree( QueryRecordContext *inContext )
3483 {
3484 DNSServiceForget( &inContext->opRef );
3485 DNSServiceForget( &inContext->mainRef );
3486 free( inContext );
3487 }
3488
3489 //===========================================================================================================================
3490 // QueryRecordPrintPrologue
3491 //===========================================================================================================================
3492
3493 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3494 {
3495 const int timeLimitSecs = inContext->timeLimitSecs;
3496 char ifName[ kInterfaceNameBufLen ];
3497
3498 InterfaceIndexToName( inContext->ifIndex, ifName );
3499
3500 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3501 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3502 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3503 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3504 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3505 FPrintF( stdout, "Time limit: " );
3506 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3507 else FPrintF( stdout, "∞\n" );
3508 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3509 FPrintF( stdout, "---\n" );
3510
3511 }
3512
3513 //===========================================================================================================================
3514 // QueryRecordCallback
3515 //===========================================================================================================================
3516
3517 static void DNSSD_API
3518 QueryRecordCallback(
3519 DNSServiceRef inSDRef,
3520 DNSServiceFlags inFlags,
3521 uint32_t inInterfaceIndex,
3522 DNSServiceErrorType inError,
3523 const char * inFullName,
3524 uint16_t inType,
3525 uint16_t inClass,
3526 uint16_t inRDataLen,
3527 const void * inRDataPtr,
3528 uint32_t inTTL,
3529 void * inContext )
3530 {
3531 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3532 struct timeval now;
3533 OSStatus err;
3534 char * rdataStr = NULL;
3535
3536 Unused( inSDRef );
3537
3538 gettimeofday( &now, NULL );
3539
3540 switch( inError )
3541 {
3542 case kDNSServiceErr_NoError:
3543 case kDNSServiceErr_NoSuchRecord:
3544 err = kNoErr;
3545 break;
3546
3547 case kDNSServiceErr_Timeout:
3548 Exit( kExitReason_Timeout );
3549
3550 default:
3551 err = inError;
3552 goto exit;
3553 }
3554
3555 if( inError != kDNSServiceErr_NoSuchRecord )
3556 {
3557 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
3558 if( !rdataStr )
3559 {
3560 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
3561 require_action( rdataStr, exit, err = kNoMemoryErr );
3562 }
3563 }
3564
3565 if( !context->printedHeader )
3566 {
3567 FPrintF( stdout, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3568 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3569 context->printedHeader = true;
3570 }
3571 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3572 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
3573 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL,
3574 rdataStr ? rdataStr : kNoSuchRecordStr );
3575
3576 if( context->oneShotMode )
3577 {
3578 if( ( inFlags & kDNSServiceFlagsAdd ) &&
3579 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
3580 {
3581 context->gotRecord = true;
3582 }
3583 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
3584 }
3585
3586 exit:
3587 FreeNullSafe( rdataStr );
3588 if( err ) exit( 1 );
3589 }
3590
3591 //===========================================================================================================================
3592 // RegisterCmd
3593 //===========================================================================================================================
3594
3595 typedef struct
3596 {
3597 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
3598 uint8_t * dataPtr; // Record data.
3599 size_t dataLen; // Record data length.
3600 uint32_t ttl; // Record TTL value.
3601 uint16_t type; // Record type.
3602
3603 } ExtraRecord;
3604
3605 typedef struct
3606 {
3607 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
3608 const char * name; // Service name argument for DNSServiceRegister().
3609 const char * type; // Service type argument for DNSServiceRegister().
3610 const char * domain; // Domain in which advertise the service.
3611 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
3612 size_t txtLen; // Service TXT record data len.
3613 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
3614 size_t extraRecordsCount; // Number of extra records.
3615 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
3616 size_t updateTXTLen; // Length of record data for TXT record update.
3617 uint32_t updateTTL; // TTL of updated TXT record.
3618 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
3619 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
3620 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
3621 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3622 uint16_t port; // Service instance's port number.
3623 Boolean printedHeader; // True if results header was printed.
3624 Boolean didRegister; // True if service was registered.
3625
3626 } RegisterContext;
3627
3628 static void RegisterPrintPrologue( const RegisterContext *inContext );
3629 static void RegisterContextFree( RegisterContext *inContext );
3630 static void DNSSD_API
3631 RegisterCallback(
3632 DNSServiceRef inSDRef,
3633 DNSServiceFlags inFlags,
3634 DNSServiceErrorType inError,
3635 const char * inName,
3636 const char * inType,
3637 const char * inDomain,
3638 void * inContext );
3639 static void RegisterUpdate( void *inContext );
3640
3641 static void RegisterCmd( void )
3642 {
3643 OSStatus err;
3644 RegisterContext * context = NULL;
3645 dispatch_source_t signalSource = NULL;
3646
3647 // Set up SIGINT handler.
3648
3649 signal( SIGINT, SIG_IGN );
3650 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3651 require_noerr( err, exit );
3652 dispatch_resume( signalSource );
3653
3654 // Create context.
3655
3656 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
3657 require_action( context, exit, err = kNoMemoryErr );
3658
3659 // Check command parameters.
3660
3661 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
3662 {
3663 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
3664 err = kParamErr;
3665 goto exit;
3666 }
3667
3668 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
3669 {
3670 FPrintF( stderr, "There are missing additional record parameters.\n" );
3671 err = kParamErr;
3672 goto exit;
3673 }
3674
3675 // Get flags.
3676
3677 context->flags = GetDNSSDFlagsFromOpts();
3678
3679 // Get interface index.
3680
3681 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3682 require_noerr_quiet( err, exit );
3683
3684 // Get TXT record data.
3685
3686 if( gRegister_TXT )
3687 {
3688 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
3689 require_noerr_quiet( err, exit );
3690 }
3691
3692 // Set remaining parameters.
3693
3694 context->name = gRegister_Name;
3695 context->type = gRegister_Type;
3696 context->domain = gRegister_Domain;
3697 context->port = (uint16_t) gRegister_Port;
3698 context->lifetimeMs = gRegister_LifetimeMs;
3699
3700 if( gAddRecord_TypesCount > 0 )
3701 {
3702 size_t i;
3703
3704 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
3705 require_action( context, exit, err = kNoMemoryErr );
3706 context->extraRecordsCount = gAddRecord_TypesCount;
3707
3708 for( i = 0; i < gAddRecord_TypesCount; ++i )
3709 {
3710 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
3711
3712 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
3713 require_noerr( err, exit );
3714
3715 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
3716 if( err )
3717 {
3718 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
3719 err = kParamErr;
3720 goto exit;
3721 }
3722
3723 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
3724 require_noerr_quiet( err, exit );
3725 }
3726 }
3727
3728 if( gUpdateRecord_Data )
3729 {
3730 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
3731 require_noerr_quiet( err, exit );
3732
3733 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
3734 context->updateDelayMs = gUpdateRecord_DelayMs;
3735 }
3736
3737 // Print prologue.
3738
3739 RegisterPrintPrologue( context );
3740
3741 // Start operation.
3742
3743 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
3744 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
3745 RegisterCallback, context );
3746 ForgetMem( &context->txtPtr );
3747 require_noerr( err, exit );
3748
3749 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3750 require_noerr( err, exit );
3751
3752 dispatch_main();
3753
3754 exit:
3755 dispatch_source_forget( &signalSource );
3756 if( context ) RegisterContextFree( context );
3757 if( err ) exit( 1 );
3758 }
3759
3760 //===========================================================================================================================
3761 // RegisterPrintPrologue
3762 //===========================================================================================================================
3763
3764 static void RegisterPrintPrologue( const RegisterContext *inContext )
3765 {
3766 size_t i;
3767 int infinite;
3768 char ifName[ kInterfaceNameBufLen ];
3769
3770 InterfaceIndexToName( inContext->ifIndex, ifName );
3771
3772 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3773 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3774 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
3775 FPrintF( stdout, "Type: %s\n", inContext->type );
3776 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3777 FPrintF( stdout, "Port: %u\n", inContext->port );
3778 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
3779 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3780 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3781 if( inContext->updateTXTPtr )
3782 {
3783 FPrintF( stdout, "\nUpdate record:\n" );
3784 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
3785 FPrintF( stdout, " TTL: %u%?s\n",
3786 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3787 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
3788 }
3789 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
3790 for( i = 0; i < inContext->extraRecordsCount; ++i )
3791 {
3792 const ExtraRecord * record = &inContext->extraRecords[ i ];
3793
3794 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
3795 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
3796 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
3797 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
3798 }
3799 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3800 FPrintF( stdout, "---\n" );
3801 }
3802
3803 //===========================================================================================================================
3804 // RegisterContextFree
3805 //===========================================================================================================================
3806
3807 static void RegisterContextFree( RegisterContext *inContext )
3808 {
3809 ExtraRecord * record;
3810 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
3811
3812 DNSServiceForget( &inContext->opRef );
3813 ForgetMem( &inContext->txtPtr );
3814 for( record = inContext->extraRecords; record < end; ++record )
3815 {
3816 check( !record->recordRef );
3817 ForgetMem( &record->dataPtr );
3818 }
3819 ForgetMem( &inContext->extraRecords );
3820 ForgetMem( &inContext->updateTXTPtr );
3821 free( inContext );
3822 }
3823
3824 //===========================================================================================================================
3825 // RegisterCallback
3826 //===========================================================================================================================
3827
3828 static void DNSSD_API
3829 RegisterCallback(
3830 DNSServiceRef inSDRef,
3831 DNSServiceFlags inFlags,
3832 DNSServiceErrorType inError,
3833 const char * inName,
3834 const char * inType,
3835 const char * inDomain,
3836 void * inContext )
3837 {
3838 RegisterContext * const context = (RegisterContext *) inContext;
3839 OSStatus err;
3840 struct timeval now;
3841
3842 Unused( inSDRef );
3843
3844 gettimeofday( &now, NULL );
3845
3846 if( !context->printedHeader )
3847 {
3848 FPrintF( stdout, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3849 context->printedHeader = true;
3850 }
3851 FPrintF( stdout, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
3852
3853 require_noerr_action_quiet( inError, exit, err = inError );
3854
3855 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
3856 {
3857 context->didRegister = true;
3858 if( context->updateTXTPtr )
3859 {
3860 if( context->updateDelayMs > 0 )
3861 {
3862 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3863 context, RegisterUpdate );
3864 }
3865 else
3866 {
3867 RegisterUpdate( context );
3868 }
3869 }
3870 if( context->extraRecordsCount > 0 )
3871 {
3872 ExtraRecord * record;
3873 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
3874
3875 for( record = context->extraRecords; record < end; ++record )
3876 {
3877 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
3878 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
3879 require_noerr( err, exit );
3880 }
3881 }
3882 if( context->lifetimeMs == 0 )
3883 {
3884 Exit( kExitReason_TimeLimit );
3885 }
3886 else if( context->lifetimeMs > 0 )
3887 {
3888 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3889 kExitReason_TimeLimit, Exit );
3890 }
3891 }
3892 err = kNoErr;
3893
3894 exit:
3895 if( err ) exit( 1 );
3896 }
3897
3898 //===========================================================================================================================
3899 // RegisterUpdate
3900 //===========================================================================================================================
3901
3902 static void RegisterUpdate( void *inContext )
3903 {
3904 OSStatus err;
3905 RegisterContext * const context = (RegisterContext *) inContext;
3906
3907 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
3908 context->updateTTL );
3909 require_noerr( err, exit );
3910
3911 exit:
3912 if( err ) exit( 1 );
3913 }
3914
3915 //===========================================================================================================================
3916 // RegisterRecordCmd
3917 //===========================================================================================================================
3918
3919 typedef struct
3920 {
3921 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
3922 DNSRecordRef recordRef; // Registered record reference.
3923 const char * recordName; // Name of resource record.
3924 uint8_t * dataPtr; // Pointer to resource record data.
3925 size_t dataLen; // Length of resource record data.
3926 uint32_t ttl; // TTL value of resource record in seconds.
3927 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3928 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3929 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3930 uint16_t recordType; // Resource record type.
3931 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3932 size_t updateDataLen; // Length of data for record update.
3933 uint32_t updateTTL; // TTL for updated record.
3934 int updateDelayMs; // Post-registration record update delay in milliseconds.
3935 Boolean didRegister; // True if the record was registered.
3936
3937 } RegisterRecordContext;
3938
3939 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3940 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3941 static void DNSSD_API
3942 RegisterRecordCallback(
3943 DNSServiceRef inSDRef,
3944 DNSRecordRef inRecordRef,
3945 DNSServiceFlags inFlags,
3946 DNSServiceErrorType inError,
3947 void * inContext );
3948 static void RegisterRecordUpdate( void *inContext );
3949
3950 static void RegisterRecordCmd( void )
3951 {
3952 OSStatus err;
3953 RegisterRecordContext * context = NULL;
3954 dispatch_source_t signalSource = NULL;
3955
3956 // Set up SIGINT handler.
3957
3958 signal( SIGINT, SIG_IGN );
3959 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3960 require_noerr( err, exit );
3961 dispatch_resume( signalSource );
3962
3963 // Create context.
3964
3965 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3966 require_action( context, exit, err = kNoMemoryErr );
3967
3968 // Create connection.
3969
3970 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3971 require_noerr_quiet( err, exit );
3972
3973 // Get flags.
3974
3975 context->flags = GetDNSSDFlagsFromOpts();
3976
3977 // Get interface.
3978
3979 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3980 require_noerr_quiet( err, exit );
3981
3982 // Get record type.
3983
3984 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3985 require_noerr( err, exit );
3986
3987 // Get record data.
3988
3989 if( gRegisterRecord_Data )
3990 {
3991 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3992 require_noerr_quiet( err, exit );
3993 }
3994
3995 // Set remaining parameters.
3996
3997 context->recordName = gRegisterRecord_Name;
3998 context->ttl = (uint32_t) gRegisterRecord_TTL;
3999 context->lifetimeMs = gRegisterRecord_LifetimeMs;
4000
4001 // Get update data.
4002
4003 if( gRegisterRecord_UpdateData )
4004 {
4005 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
4006 require_noerr_quiet( err, exit );
4007
4008 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
4009 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
4010 }
4011
4012 // Print prologue.
4013
4014 RegisterRecordPrintPrologue( context );
4015
4016 // Start operation.
4017
4018 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
4019 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
4020 context->ttl, RegisterRecordCallback, context );
4021 if( err )
4022 {
4023 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
4024 goto exit;
4025 }
4026
4027 dispatch_main();
4028
4029 exit:
4030 dispatch_source_forget( &signalSource );
4031 if( context ) RegisterRecordContextFree( context );
4032 if( err ) exit( 1 );
4033 }
4034
4035 //===========================================================================================================================
4036 // RegisterRecordPrintPrologue
4037 //===========================================================================================================================
4038
4039 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
4040 {
4041 int infinite;
4042 char ifName[ kInterfaceNameBufLen ];
4043
4044 InterfaceIndexToName( inContext->ifIndex, ifName );
4045
4046 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4047 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4048 FPrintF( stdout, "Name: %s\n", inContext->recordName );
4049 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
4050 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
4051 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
4052 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
4053 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
4054 if( inContext->updateDataPtr )
4055 {
4056 FPrintF( stdout, "\nUpdate record:\n" );
4057 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
4058 FPrintF( stdout, " TTL: %u%?s\n",
4059 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4060 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
4061 }
4062 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4063 FPrintF( stdout, "---\n" );
4064 }
4065
4066 //===========================================================================================================================
4067 // RegisterRecordContextFree
4068 //===========================================================================================================================
4069
4070 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
4071 {
4072 DNSServiceForget( &inContext->conRef );
4073 ForgetMem( &inContext->dataPtr );
4074 ForgetMem( &inContext->updateDataPtr );
4075 free( inContext );
4076 }
4077
4078 //===========================================================================================================================
4079 // RegisterRecordCallback
4080 //===========================================================================================================================
4081
4082 static void
4083 RegisterRecordCallback(
4084 DNSServiceRef inSDRef,
4085 DNSRecordRef inRecordRef,
4086 DNSServiceFlags inFlags,
4087 DNSServiceErrorType inError,
4088 void * inContext )
4089 {
4090 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4091 struct timeval now;
4092
4093 Unused( inSDRef );
4094 Unused( inRecordRef );
4095 Unused( inFlags );
4096 Unused( context );
4097
4098 gettimeofday( &now, NULL );
4099 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4100
4101 if( !context->didRegister && !inError )
4102 {
4103 context->didRegister = true;
4104 if( context->updateDataPtr )
4105 {
4106 if( context->updateDelayMs > 0 )
4107 {
4108 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4109 context, RegisterRecordUpdate );
4110 }
4111 else
4112 {
4113 RegisterRecordUpdate( context );
4114 }
4115 }
4116 if( context->lifetimeMs == 0 )
4117 {
4118 Exit( kExitReason_TimeLimit );
4119 }
4120 else if( context->lifetimeMs > 0 )
4121 {
4122 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4123 kExitReason_TimeLimit, Exit );
4124 }
4125 }
4126 }
4127
4128 //===========================================================================================================================
4129 // RegisterRecordUpdate
4130 //===========================================================================================================================
4131
4132 static void RegisterRecordUpdate( void *inContext )
4133 {
4134 OSStatus err;
4135 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4136
4137 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4138 context->updateDataPtr, context->updateTTL );
4139 require_noerr( err, exit );
4140
4141 exit:
4142 if( err ) exit( 1 );
4143 }
4144
4145 //===========================================================================================================================
4146 // ResolveCmd
4147 //===========================================================================================================================
4148
4149 typedef struct
4150 {
4151 DNSServiceRef mainRef; // Main sdRef for shared connections.
4152 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4153 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4154 const char * name; // Service name argument for DNSServiceResolve().
4155 const char * type; // Service type argument for DNSServiceResolve().
4156 const char * domain; // Domain argument for DNSServiceResolve().
4157 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4158 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4159
4160 } ResolveContext;
4161
4162 static void ResolvePrintPrologue( const ResolveContext *inContext );
4163 static void ResolveContextFree( ResolveContext *inContext );
4164 static void DNSSD_API
4165 ResolveCallback(
4166 DNSServiceRef inSDRef,
4167 DNSServiceFlags inFlags,
4168 uint32_t inInterfaceIndex,
4169 DNSServiceErrorType inError,
4170 const char * inFullName,
4171 const char * inHostname,
4172 uint16_t inPort,
4173 uint16_t inTXTLen,
4174 const unsigned char * inTXTPtr,
4175 void * inContext );
4176
4177 static void ResolveCmd( void )
4178 {
4179 OSStatus err;
4180 DNSServiceRef sdRef;
4181 ResolveContext * context = NULL;
4182 dispatch_source_t signalSource = NULL;
4183 int useMainConnection;
4184
4185 // Set up SIGINT handler.
4186
4187 signal( SIGINT, SIG_IGN );
4188 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4189 require_noerr( err, exit );
4190 dispatch_resume( signalSource );
4191
4192 // Create context.
4193
4194 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4195 require_action( context, exit, err = kNoMemoryErr );
4196
4197 // Check command parameters.
4198
4199 if( gResolve_TimeLimitSecs < 0 )
4200 {
4201 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4202 err = kParamErr;
4203 goto exit;
4204 }
4205
4206 // Create main connection.
4207
4208 if( gConnectionOpt )
4209 {
4210 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4211 require_noerr_quiet( err, exit );
4212 useMainConnection = true;
4213 }
4214 else
4215 {
4216 useMainConnection = false;
4217 }
4218
4219 // Get flags.
4220
4221 context->flags = GetDNSSDFlagsFromOpts();
4222 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4223
4224 // Get interface index.
4225
4226 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4227 require_noerr_quiet( err, exit );
4228
4229 // Set remaining parameters.
4230
4231 context->name = gResolve_Name;
4232 context->type = gResolve_Type;
4233 context->domain = gResolve_Domain;
4234 context->timeLimitSecs = gResolve_TimeLimitSecs;
4235
4236 // Print prologue.
4237
4238 ResolvePrintPrologue( context );
4239
4240 // Start operation.
4241
4242 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4243 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4244 ResolveCallback, NULL );
4245 require_noerr( err, exit );
4246
4247 context->opRef = sdRef;
4248 if( !useMainConnection )
4249 {
4250 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4251 require_noerr( err, exit );
4252 }
4253
4254 // Set time limit.
4255
4256 if( context->timeLimitSecs > 0 )
4257 {
4258 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4259 kExitReason_TimeLimit, Exit );
4260 }
4261 dispatch_main();
4262
4263 exit:
4264 dispatch_source_forget( &signalSource );
4265 if( context ) ResolveContextFree( context );
4266 if( err ) exit( 1 );
4267 }
4268
4269 //===========================================================================================================================
4270 // ReconfirmCmd
4271 //===========================================================================================================================
4272
4273 static void ReconfirmCmd( void )
4274 {
4275 OSStatus err;
4276 uint8_t * rdataPtr = NULL;
4277 size_t rdataLen = 0;
4278 DNSServiceFlags flags;
4279 uint32_t ifIndex;
4280 uint16_t type, class;
4281 char ifName[ kInterfaceNameBufLen ];
4282
4283 // Get flags.
4284
4285 flags = GetDNSSDFlagsFromOpts();
4286
4287 // Get interface index.
4288
4289 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4290 require_noerr_quiet( err, exit );
4291
4292 // Get record type.
4293
4294 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4295 require_noerr( err, exit );
4296
4297 // Get record data.
4298
4299 if( gReconfirmRecord_Data )
4300 {
4301 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4302 require_noerr_quiet( err, exit );
4303 }
4304
4305 // Get record class.
4306
4307 if( gReconfirmRecord_Class )
4308 {
4309 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4310 require_noerr( err, exit );
4311 }
4312 else
4313 {
4314 class = kDNSServiceClass_IN;
4315 }
4316
4317 // Print prologue.
4318
4319 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4320 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4321 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4322 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4323 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4324 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4325 FPrintF( stdout, "---\n" );
4326
4327 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4328 FPrintF( stdout, "Error: %#m\n", err );
4329
4330 exit:
4331 FreeNullSafe( rdataPtr );
4332 if( err ) exit( 1 );
4333 }
4334
4335 //===========================================================================================================================
4336 // ResolvePrintPrologue
4337 //===========================================================================================================================
4338
4339 static void ResolvePrintPrologue( const ResolveContext *inContext )
4340 {
4341 const int timeLimitSecs = inContext->timeLimitSecs;
4342 char ifName[ kInterfaceNameBufLen ];
4343
4344 InterfaceIndexToName( inContext->ifIndex, ifName );
4345
4346 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4347 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4348 FPrintF( stdout, "Name: %s\n", inContext->name );
4349 FPrintF( stdout, "Type: %s\n", inContext->type );
4350 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4351 FPrintF( stdout, "Time limit: " );
4352 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4353 else FPrintF( stdout, "∞\n" );
4354 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4355 FPrintF( stdout, "---\n" );
4356 }
4357
4358 //===========================================================================================================================
4359 // ResolveContextFree
4360 //===========================================================================================================================
4361
4362 static void ResolveContextFree( ResolveContext *inContext )
4363 {
4364 DNSServiceForget( &inContext->opRef );
4365 DNSServiceForget( &inContext->mainRef );
4366 free( inContext );
4367 }
4368
4369 //===========================================================================================================================
4370 // ResolveCallback
4371 //===========================================================================================================================
4372
4373 static void DNSSD_API
4374 ResolveCallback(
4375 DNSServiceRef inSDRef,
4376 DNSServiceFlags inFlags,
4377 uint32_t inInterfaceIndex,
4378 DNSServiceErrorType inError,
4379 const char * inFullName,
4380 const char * inHostname,
4381 uint16_t inPort,
4382 uint16_t inTXTLen,
4383 const unsigned char * inTXTPtr,
4384 void * inContext )
4385 {
4386 struct timeval now;
4387 char errorStr[ 64 ];
4388
4389 Unused( inSDRef );
4390 Unused( inFlags );
4391 Unused( inContext );
4392
4393 gettimeofday( &now, NULL );
4394
4395 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4396
4397 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4398 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4399 if( inTXTLen == 1 )
4400 {
4401 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4402 }
4403 else
4404 {
4405 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4406 }
4407 }
4408
4409 //===========================================================================================================================
4410 // GetAddrInfoPOSIXCmd
4411 //===========================================================================================================================
4412
4413 #define AddressFamilyStr( X ) ( \
4414 ( (X) == AF_INET ) ? "inet" : \
4415 ( (X) == AF_INET6 ) ? "inet6" : \
4416 ( (X) == AF_UNSPEC ) ? "unspec" : \
4417 "???" )
4418
4419 typedef struct
4420 {
4421 unsigned int flag;
4422 const char * str;
4423
4424 } FlagStringPair;
4425
4426 #define CaseFlagStringify( X ) { (X), # X }
4427
4428 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4429 {
4430 #if( defined( AI_UNUSABLE ) )
4431 CaseFlagStringify( AI_UNUSABLE ),
4432 #endif
4433 CaseFlagStringify( AI_NUMERICSERV ),
4434 CaseFlagStringify( AI_V4MAPPED ),
4435 CaseFlagStringify( AI_ADDRCONFIG ),
4436 #if( defined( AI_V4MAPPED_CFG ) )
4437 CaseFlagStringify( AI_V4MAPPED_CFG ),
4438 #endif
4439 CaseFlagStringify( AI_ALL ),
4440 CaseFlagStringify( AI_NUMERICHOST ),
4441 CaseFlagStringify( AI_CANONNAME ),
4442 CaseFlagStringify( AI_PASSIVE ),
4443 { 0, NULL }
4444 };
4445
4446 static void GetAddrInfoPOSIXCmd( void )
4447 {
4448 OSStatus err;
4449 struct addrinfo hints;
4450 struct timeval now;
4451 const struct addrinfo * addrInfo;
4452 struct addrinfo * addrInfoList = NULL;
4453 const FlagStringPair * pair;
4454
4455 memset( &hints, 0, sizeof( hints ) );
4456 hints.ai_socktype = SOCK_STREAM;
4457
4458 // Set hints address family.
4459
4460 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4461 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4462 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4463 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4464 else
4465 {
4466 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4467 err = kParamErr;
4468 goto exit;
4469 }
4470
4471 // Set hints flags.
4472
4473 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4474 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4475 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4476 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4477 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4478 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4479 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4480 #if( defined( AI_V4MAPPED_CFG ) )
4481 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4482 #endif
4483 #if( defined( AI_DEFAULT ) )
4484 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4485 #endif
4486 #if( defined( AI_UNUSABLE ) )
4487 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4488 #endif
4489
4490 // Print prologue.
4491
4492 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4493 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4494 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4495 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4496 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4497 {
4498 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4499 }
4500 FPrintF( stdout, ">\n" );
4501 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4502 FPrintF( stdout, "---\n" );
4503
4504 // Call getaddrinfo().
4505
4506 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4507 gettimeofday( &now, NULL );
4508 if( err )
4509 {
4510 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4511 }
4512 else
4513 {
4514 int addrCount = 0;
4515
4516 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4517
4518 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4519 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4520 {
4521 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4522 }
4523 }
4524 FPrintF( stdout, "---\n" );
4525 FPrintF( stdout, "End time: %{du:time}\n", &now );
4526
4527 exit:
4528 if( addrInfoList ) freeaddrinfo( addrInfoList );
4529 if( err ) exit( 1 );
4530 }
4531
4532 //===========================================================================================================================
4533 // ReverseLookupCmd
4534 //===========================================================================================================================
4535
4536 #define kIP6ARPADomainStr "ip6.arpa."
4537
4538 static void ReverseLookupCmd( void )
4539 {
4540 OSStatus err;
4541 QueryRecordContext * context = NULL;
4542 DNSServiceRef sdRef;
4543 dispatch_source_t signalSource = NULL;
4544 uint32_t ipv4Addr;
4545 uint8_t ipv6Addr[ 16 ];
4546 char recordName[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr ) ];
4547 int useMainConnection;
4548 const char * endPtr;
4549
4550 // Set up SIGINT handler.
4551
4552 signal( SIGINT, SIG_IGN );
4553 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4554 require_noerr( err, exit );
4555 dispatch_resume( signalSource );
4556
4557 // Create context.
4558
4559 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
4560 require_action( context, exit, err = kNoMemoryErr );
4561
4562 // Check command parameters.
4563
4564 if( gReverseLookup_TimeLimitSecs < 0 )
4565 {
4566 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
4567 err = kParamErr;
4568 goto exit;
4569 }
4570
4571 // Create main connection.
4572
4573 if( gConnectionOpt )
4574 {
4575 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4576 require_noerr_quiet( err, exit );
4577 useMainConnection = true;
4578 }
4579 else
4580 {
4581 useMainConnection = false;
4582 }
4583
4584 // Get flags.
4585
4586 context->flags = GetDNSSDFlagsFromOpts();
4587 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4588
4589 // Get interface index.
4590
4591 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4592 require_noerr_quiet( err, exit );
4593
4594 // Create reverse lookup record name.
4595
4596 err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
4597 &ipv4Addr, NULL, NULL, NULL, &endPtr );
4598 if( err || ( *endPtr != '\0' ) )
4599 {
4600 char * dst;
4601 int i;
4602
4603 err = _StringToIPv6Address( gReverseLookup_IPAddr,
4604 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
4605 ipv6Addr, NULL, NULL, NULL, &endPtr );
4606 if( err || ( *endPtr != '\0' ) )
4607 {
4608 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
4609 err = kParamErr;
4610 goto exit;
4611 }
4612 dst = recordName;
4613 for( i = 15; i >= 0; --i )
4614 {
4615 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
4616 *dst++ = '.';
4617 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
4618 *dst++ = '.';
4619 }
4620 strcpy_literal( dst, kIP6ARPADomainStr );
4621 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
4622 }
4623 else
4624 {
4625 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
4626 ipv4Addr & 0xFF,
4627 ( ipv4Addr >> 8 ) & 0xFF,
4628 ( ipv4Addr >> 16 ) & 0xFF,
4629 ( ipv4Addr >> 24 ) & 0xFF );
4630 }
4631
4632 // Set remaining parameters.
4633
4634 context->recordName = recordName;
4635 context->recordType = kDNSServiceType_PTR;
4636 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
4637 context->oneShotMode = gReverseLookup_OneShot ? true : false;
4638
4639 // Print prologue.
4640
4641 QueryRecordPrintPrologue( context );
4642
4643 // Start operation.
4644
4645 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4646 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
4647 kDNSServiceClass_IN, QueryRecordCallback, context );
4648 require_noerr( err, exit );
4649
4650 context->opRef = sdRef;
4651 if( !useMainConnection )
4652 {
4653 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4654 require_noerr( err, exit );
4655 }
4656
4657 // Set time limit.
4658
4659 if( context->timeLimitSecs > 0 )
4660 {
4661 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4662 kExitReason_TimeLimit, Exit );
4663 }
4664 dispatch_main();
4665
4666 exit:
4667 dispatch_source_forget( &signalSource );
4668 if( context ) QueryRecordContextFree( context );
4669 if( err ) exit( 1 );
4670 }
4671
4672 //===========================================================================================================================
4673 // PortMappingCmd
4674 //===========================================================================================================================
4675
4676 typedef struct
4677 {
4678 DNSServiceRef mainRef; // Main sdRef for shared connection.
4679 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
4680 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
4681 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4682 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4683 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
4684 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4685 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
4686 Boolean printedHeader; // True if results header was printed.
4687
4688 } PortMappingContext;
4689
4690 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
4691 static void PortMappingContextFree( PortMappingContext *inContext );
4692 static void DNSSD_API
4693 PortMappingCallback(
4694 DNSServiceRef inSDRef,
4695 DNSServiceFlags inFlags,
4696 uint32_t inInterfaceIndex,
4697 DNSServiceErrorType inError,
4698 uint32_t inExternalIPv4Address,
4699 DNSServiceProtocol inProtocol,
4700 uint16_t inInternalPort,
4701 uint16_t inExternalPort,
4702 uint32_t inTTL,
4703 void * inContext );
4704
4705 static void PortMappingCmd( void )
4706 {
4707 OSStatus err;
4708 PortMappingContext * context = NULL;
4709 DNSServiceRef sdRef;
4710 dispatch_source_t signalSource = NULL;
4711 int useMainConnection;
4712
4713 // Set up SIGINT handler.
4714
4715 signal( SIGINT, SIG_IGN );
4716 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4717 require_noerr( err, exit );
4718 dispatch_resume( signalSource );
4719
4720 // Create context.
4721
4722 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
4723 require_action( context, exit, err = kNoMemoryErr );
4724
4725 // Check command parameters.
4726
4727 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
4728 {
4729 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
4730 err = kParamErr;
4731 goto exit;
4732 }
4733
4734 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
4735 {
4736 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
4737 err = kParamErr;
4738 goto exit;
4739 }
4740
4741 // Create main connection.
4742
4743 if( gConnectionOpt )
4744 {
4745 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4746 require_noerr_quiet( err, exit );
4747 useMainConnection = true;
4748 }
4749 else
4750 {
4751 useMainConnection = false;
4752 }
4753
4754 // Get flags.
4755
4756 context->flags = GetDNSSDFlagsFromOpts();
4757 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4758
4759 // Get interface index.
4760
4761 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4762 require_noerr_quiet( err, exit );
4763
4764 // Set remaining parameters.
4765
4766 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
4767 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
4768 context->ttl = (uint32_t) gPortMapping_TTL;
4769 context->internalPort = (uint16_t) gPortMapping_InternalPort;
4770 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
4771
4772 // Print prologue.
4773
4774 PortMappingPrintPrologue( context );
4775
4776 // Start operation.
4777
4778 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4779 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
4780 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
4781 require_noerr( err, exit );
4782
4783 context->opRef = sdRef;
4784 if( !useMainConnection )
4785 {
4786 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4787 require_noerr( err, exit );
4788 }
4789
4790 dispatch_main();
4791
4792 exit:
4793 dispatch_source_forget( &signalSource );
4794 if( context ) PortMappingContextFree( context );
4795 if( err ) exit( 1 );
4796 }
4797
4798 //===========================================================================================================================
4799 // PortMappingPrintPrologue
4800 //===========================================================================================================================
4801
4802 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
4803 {
4804 char ifName[ kInterfaceNameBufLen ];
4805
4806 InterfaceIndexToName( inContext->ifIndex, ifName );
4807
4808 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4809 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4810 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
4811 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
4812 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
4813 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
4814 " (system will use a default value.)" );
4815 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4816 FPrintF( stdout, "---\n" );
4817
4818 }
4819
4820 //===========================================================================================================================
4821 // PortMappingContextFree
4822 //===========================================================================================================================
4823
4824 static void PortMappingContextFree( PortMappingContext *inContext )
4825 {
4826 DNSServiceForget( &inContext->opRef );
4827 DNSServiceForget( &inContext->mainRef );
4828 free( inContext );
4829 }
4830
4831 //===========================================================================================================================
4832 // PortMappingCallback
4833 //===========================================================================================================================
4834
4835 static void DNSSD_API
4836 PortMappingCallback(
4837 DNSServiceRef inSDRef,
4838 DNSServiceFlags inFlags,
4839 uint32_t inInterfaceIndex,
4840 DNSServiceErrorType inError,
4841 uint32_t inExternalIPv4Address,
4842 DNSServiceProtocol inProtocol,
4843 uint16_t inInternalPort,
4844 uint16_t inExternalPort,
4845 uint32_t inTTL,
4846 void * inContext )
4847 {
4848 PortMappingContext * const context = (PortMappingContext *) inContext;
4849 struct timeval now;
4850 char errorStr[ 128 ];
4851
4852 Unused( inSDRef );
4853 Unused( inFlags );
4854
4855 gettimeofday( &now, NULL );
4856
4857 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
4858 if( !context->printedHeader )
4859 {
4860 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4861 context->printedHeader = true;
4862 }
4863 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4864 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
4865 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
4866 }
4867
4868 #if( TARGET_OS_DARWIN )
4869 //===========================================================================================================================
4870 // RegisterKACmd
4871 //===========================================================================================================================
4872
4873 typedef struct
4874 {
4875 dispatch_queue_t queue; // Serial queue for command's events.
4876 dispatch_semaphore_t doneSem; // Semaphore to signal when underlying command operation is done.
4877 sockaddr_ip local; // Connection's local IP address and port.
4878 sockaddr_ip remote; // Connection's remote IP address and port.
4879 DNSServiceFlags flags; // Flags to pass to DNSServiceSleepKeepalive_sockaddr().
4880 unsigned int timeout; // Timeout to pass to DNSServiceSleepKeepalive_sockaddr().
4881 DNSServiceRef keepalive; // DNSServiceSleepKeepalive_sockaddr operation.
4882 dispatch_source_t sourceSigInt; // Dispatch source for SIGINT.
4883 dispatch_source_t sourceSigTerm; // Dispatch source for SIGTERM.
4884 OSStatus error; // Command's error.
4885
4886 } RegisterKACmdContext;
4887
4888 static void _RegisterKACmdFree( RegisterKACmdContext *inCmd );
4889 static void _RegisterKACmdStart( void *inContext );
4890 static OSStatus _RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA );
4891
4892 static void RegisterKACmd( void )
4893 {
4894 OSStatus err;
4895 RegisterKACmdContext * cmd = NULL;
4896
4897 if( !SOFT_LINK_HAS_FUNCTION( system_dnssd, DNSServiceSleepKeepalive_sockaddr ) )
4898 {
4899 FPrintF( stderr, "error: Failed to soft link DNSServiceSleepKeepalive_sockaddr from libsystem_dnssd.\n" );
4900 err = kNotFoundErr;
4901 goto exit;
4902 }
4903 cmd = (RegisterKACmdContext *) calloc( 1, sizeof( *cmd ) );
4904 require_action( cmd, exit, err = kNoMemoryErr );
4905
4906 err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_LocalAddress, "local IP address", &cmd->local );
4907 require_noerr_quiet( err, exit );
4908
4909 err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_RemoteAddress, "remote IP address", &cmd->remote );
4910 require_noerr_quiet( err, exit );
4911
4912 err = CheckIntegerArgument( gRegisterKA_Timeout, "timeout", 0, INT_MAX );
4913 require_noerr_quiet( err, exit );
4914
4915 cmd->flags = GetDNSSDFlagsFromOpts();
4916 cmd->timeout = (unsigned int) gRegisterKA_Timeout;
4917
4918 // Start command.
4919
4920 cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.registerka-command", DISPATCH_QUEUE_SERIAL );
4921 require_action( cmd->queue, exit, err = kNoResourcesErr );
4922
4923 cmd->doneSem = dispatch_semaphore_create( 0 );
4924 require_action( cmd->doneSem, exit, err = kNoResourcesErr );
4925
4926 dispatch_async_f( cmd->queue, cmd, _RegisterKACmdStart );
4927 dispatch_semaphore_wait( cmd->doneSem, DISPATCH_TIME_FOREVER );
4928 if( cmd->error ) err = cmd->error;
4929
4930 FPrintF( stdout, "---\n" );
4931 FPrintF( stdout, "End time: %{du:time}\n", NULL );
4932
4933 exit:
4934 if( cmd ) _RegisterKACmdFree( cmd );
4935 gExitCode = err ? 1 : 0;
4936 }
4937
4938 //===========================================================================================================================
4939
4940 static void _RegisterKACmdFree( RegisterKACmdContext *inCmd )
4941 {
4942 check( !inCmd->keepalive );
4943 check( !inCmd->sourceSigInt );
4944 check( !inCmd->sourceSigTerm );
4945 dispatch_forget( &inCmd->queue );
4946 dispatch_forget( &inCmd->doneSem );
4947 free( inCmd );
4948 }
4949
4950 //===========================================================================================================================
4951
4952 static void _RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError );
4953 static void _RegisterKACmdSignalHandler( void *inContext );
4954 static void DNSSD_API _RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx );
4955
4956 static void _RegisterKACmdStart( void *inContext )
4957 {
4958 OSStatus err;
4959 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inContext;
4960
4961 signal( SIGINT, SIG_IGN );
4962 err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigInt );
4963 require_noerr( err, exit );
4964 dispatch_resume( cmd->sourceSigInt );
4965
4966 signal( SIGTERM, SIG_IGN );
4967 err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigTerm );
4968 require_noerr( err, exit );
4969 dispatch_resume( cmd->sourceSigTerm );
4970
4971 FPrintF( stdout, "Flags: %#{flags}\n", cmd->flags, kDNSServiceFlagsDescriptors );
4972 FPrintF( stdout, "Local: %##a\n", &cmd->local.sa );
4973 FPrintF( stdout, "Remote: %##a\n", &cmd->remote.sa );
4974 FPrintF( stdout, "Timeout: %u\n", cmd->timeout );
4975 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4976 FPrintF( stdout, "---\n" );
4977
4978 err = soft_DNSServiceSleepKeepalive_sockaddr( &cmd->keepalive, cmd->flags, &cmd->local.sa, &cmd->remote.sa,
4979 cmd->timeout, _RegisterKACmdKeepaliveCallback, cmd );
4980 require_noerr( err, exit );
4981
4982 err = DNSServiceSetDispatchQueue( cmd->keepalive, cmd->queue );
4983 require_noerr( err, exit );
4984
4985 exit:
4986 if( err ) _RegisterKACmdStop( cmd, err );
4987 }
4988
4989 //===========================================================================================================================
4990
4991 static OSStatus _RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA )
4992 {
4993 OSStatus err;
4994 sockaddr_ip sip;
4995
4996 err = StringToSockAddr( inArgStr, &sip, sizeof( sip ), NULL );
4997 if( !err && ( ( sip.sa.sa_family == AF_INET ) || ( sip.sa.sa_family == AF_INET6 ) ) )
4998 {
4999 if( outSA ) SockAddrCopy( &sip, outSA );
5000 }
5001 else
5002 {
5003 FPrintF( stderr, "error: Invalid %s: '%s'\n", inArgName, inArgStr );
5004 err = kParamErr;
5005 }
5006 return( err );
5007 }
5008
5009 //===========================================================================================================================
5010
5011 static void _RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError )
5012 {
5013 if( !inCmd->error ) inCmd->error = inError;
5014 DNSServiceForget( &inCmd->keepalive );
5015 dispatch_source_forget( &inCmd->sourceSigInt );
5016 dispatch_source_forget( &inCmd->sourceSigTerm );
5017 dispatch_semaphore_signal( inCmd->doneSem );
5018 }
5019
5020 //===========================================================================================================================
5021
5022 static void _RegisterKACmdSignalHandler( void *inContext )
5023 {
5024 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inContext;
5025
5026 _RegisterKACmdStop( cmd, kNoErr );
5027 }
5028
5029 //===========================================================================================================================
5030
5031 static void DNSSD_API _RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
5032 {
5033 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inCtx;
5034
5035 Unused( inSDRef );
5036
5037 FPrintF( stdout, "%{du:time} Record registration result: %#m\n", NULL, inError );
5038 if( !cmd->error ) cmd->error = inError;
5039 }
5040 #endif // TARGET_OS_DARWIN
5041
5042 //===========================================================================================================================
5043 // BrowseAllCmd
5044 //===========================================================================================================================
5045
5046 typedef struct BrowseAllConnection BrowseAllConnection;
5047
5048 typedef struct
5049 {
5050 ServiceBrowserRef browser; // Service browser.
5051 ServiceBrowserResults * results; // Results from the service browser.
5052 BrowseAllConnection * connectionList; // List of connections.
5053 dispatch_source_t connectionTimer; // Timer for connection timeout.
5054 int connectionPendingCount; // Number of pending connections.
5055 int connectionTimeoutSecs; // Timeout value for connections in seconds.
5056
5057 } BrowseAllContext;
5058
5059 struct BrowseAllConnection
5060 {
5061 BrowseAllConnection * next; // Next connection object in list.
5062 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
5063 uint16_t port; // TCP port to connect to.
5064 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
5065 OSStatus status; // Status of connection. NoErr means connection succeeded.
5066 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
5067 int32_t refCount; // This object's reference count.
5068 BrowseAllContext * context; // Back pointer to parent context.
5069 };
5070
5071 static void _BrowseAllContextFree( BrowseAllContext *inContext );
5072 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
5073 static OSStatus
5074 _BrowseAllConnectionCreate(
5075 const struct sockaddr * inSockAddr,
5076 uint16_t inPort,
5077 BrowseAllContext * inContext,
5078 BrowseAllConnection ** outConnection );
5079 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
5080 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
5081 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
5082 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
5083 static void _BrowseAllExit( void *inContext );
5084
5085 static Boolean _IsServiceTypeTCP( const char *inServiceType );
5086
5087 static void BrowseAllCmd( void )
5088 {
5089 OSStatus err;
5090 BrowseAllContext * context = NULL;
5091 size_t i;
5092 uint32_t ifIndex;
5093 char ifName[ kInterfaceNameBufLen ];
5094
5095 // Check parameters.
5096
5097 if( gBrowseAll_BrowseTimeSecs <= 0 )
5098 {
5099 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
5100 err = kParamErr;
5101 goto exit;
5102 }
5103
5104 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
5105 require_action( context, exit, err = kNoMemoryErr );
5106
5107 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
5108 #if( TARGET_OS_POSIX )
5109 // Increase the open file descriptor limit for connection sockets.
5110
5111 if( context->connectionTimeoutSecs > 0 )
5112 {
5113 struct rlimit fdLimits;
5114
5115 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
5116 err = map_global_noerr_errno( err );
5117 require_noerr( err, exit );
5118
5119 if( fdLimits.rlim_cur < 4096 )
5120 {
5121 fdLimits.rlim_cur = 4096;
5122 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
5123 err = map_global_noerr_errno( err );
5124 require_noerr( err, exit );
5125 }
5126 }
5127 #endif
5128
5129 // Get interface index.
5130
5131 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5132 require_noerr_quiet( err, exit );
5133
5134 // Print prologue.
5135
5136 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5137 FPrintF( stdout, "Service types: ");
5138 if( gBrowseAll_ServiceTypesCount > 0 )
5139 {
5140 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
5141 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
5142 {
5143 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
5144 }
5145 FPrintF( stdout, "\n" );
5146 }
5147 else
5148 {
5149 FPrintF( stdout, "all services\n" );
5150 }
5151 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
5152 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
5153 FPrintF( stdout, "Connect timeout: %d second%?c\n",
5154 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
5155 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
5156 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5157 FPrintF( stdout, "---\n" );
5158
5159 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
5160 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
5161 require_noerr( err, exit );
5162
5163 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
5164 {
5165 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
5166 require_noerr( err, exit );
5167 }
5168 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
5169 ServiceBrowserStart( context->browser );
5170 dispatch_main();
5171
5172 exit:
5173 if( context ) _BrowseAllContextFree( context );
5174 if( err ) exit( 1 );
5175 }
5176
5177 //===========================================================================================================================
5178 // _BrowseAllContextFree
5179 //===========================================================================================================================
5180
5181 static void _BrowseAllContextFree( BrowseAllContext *inContext )
5182 {
5183 check( !inContext->browser );
5184 check( !inContext->connectionTimer );
5185 check( !inContext->connectionList );
5186 ForgetServiceBrowserResults( &inContext->results );
5187 free( inContext );
5188 }
5189
5190 //===========================================================================================================================
5191 // _BrowseAllServiceBrowserCallback
5192 //===========================================================================================================================
5193
5194 #define kDiscardProtocolPort 9
5195
5196 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
5197 {
5198 OSStatus err;
5199 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5200 SBRDomain * domain;
5201 SBRServiceType * type;
5202 SBRServiceInstance * instance;
5203 SBRIPAddress * ipaddr;
5204
5205 Unused( inError );
5206
5207 require_action( inResults, exit, err = kUnexpectedErr );
5208
5209 check( !context->results );
5210 context->results = inResults;
5211 ServiceBrowserResultsRetain( context->results );
5212
5213 check( context->connectionPendingCount == 0 );
5214 if( context->connectionTimeoutSecs > 0 )
5215 {
5216 BrowseAllConnection * connection;
5217 BrowseAllConnection ** connectionPtr = &context->connectionList;
5218 char destination[ kSockAddrStringMaxSize ];
5219
5220 for( domain = context->results->domainList; domain; domain = domain->next )
5221 {
5222 for( type = domain->typeList; type; type = type->next )
5223 {
5224 if( !_IsServiceTypeTCP( type->name ) ) continue;
5225 for( instance = type->instanceList; instance; instance = instance->next )
5226 {
5227 if( instance->port == kDiscardProtocolPort ) continue;
5228 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5229 {
5230 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
5231 require_noerr( err, exit );
5232
5233 *connectionPtr = connection;
5234 connectionPtr = &connection->next;
5235
5236 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
5237 check_noerr( err );
5238 if( !err )
5239 {
5240 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
5241 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
5242 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5243 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
5244 dispatch_get_main_queue() );
5245 check_noerr( err );
5246 }
5247 if( !err )
5248 {
5249 _BrowseAllConnectionRetain( connection );
5250 connection->status = kInProgressErr;
5251 ++context->connectionPendingCount;
5252 }
5253 else
5254 {
5255 connection->status = err;
5256 }
5257 }
5258 }
5259 }
5260 }
5261 }
5262
5263 if( context->connectionPendingCount > 0 )
5264 {
5265 check( !context->connectionTimer );
5266 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5267 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5268 require_noerr( err, exit );
5269 dispatch_resume( context->connectionTimer );
5270 }
5271 else
5272 {
5273 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5274 }
5275 err = kNoErr;
5276
5277 exit:
5278 ForgetCF( &context->browser );
5279 if( err ) exit( 1 );
5280 }
5281
5282 //===========================================================================================================================
5283 // _BrowseAllConnectionCreate
5284 //===========================================================================================================================
5285
5286 static OSStatus
5287 _BrowseAllConnectionCreate(
5288 const struct sockaddr * inSockAddr,
5289 uint16_t inPort,
5290 BrowseAllContext * inContext,
5291 BrowseAllConnection ** outConnection )
5292 {
5293 OSStatus err;
5294 BrowseAllConnection * obj;
5295
5296 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5297 require_action( obj, exit, err = kNoMemoryErr );
5298
5299 obj->refCount = 1;
5300 SockAddrCopy( inSockAddr, &obj->sip );
5301 obj->port = inPort;
5302 obj->context = inContext;
5303
5304 *outConnection = obj;
5305 err = kNoErr;
5306
5307 exit:
5308 return( err );
5309 }
5310
5311 //===========================================================================================================================
5312 // _BrowseAllConnectionRetain
5313 //===========================================================================================================================
5314
5315 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5316 {
5317 ++inConnection->refCount;
5318 }
5319
5320 //===========================================================================================================================
5321 // _BrowseAllConnectionRelease
5322 //===========================================================================================================================
5323
5324 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5325 {
5326 if( --inConnection->refCount == 0 ) free( inConnection );
5327 }
5328
5329 //===========================================================================================================================
5330 // _BrowseAllConnectionProgress
5331 //===========================================================================================================================
5332
5333 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5334 {
5335 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5336
5337 if( inPhase == kAsyncConnectionPhase_Connected )
5338 {
5339 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5340
5341 connection->connectTimeSecs = info->connectSecs;
5342 }
5343 }
5344
5345 //===========================================================================================================================
5346 // _BrowseAllConnectionHandler
5347 //===========================================================================================================================
5348
5349 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5350 {
5351 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5352 BrowseAllContext * const context = connection->context;
5353
5354 connection->status = inError;
5355 ForgetSocket( &inSock );
5356 if( context )
5357 {
5358 check( context->connectionPendingCount > 0 );
5359 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5360 {
5361 dispatch_source_forget( &context->connectionTimer );
5362 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5363 }
5364 }
5365 _BrowseAllConnectionRelease( connection );
5366 }
5367
5368 //===========================================================================================================================
5369 // _BrowseAllExit
5370 //===========================================================================================================================
5371
5372 #define Indent( X ) ( (X) * 4 ), ""
5373
5374 static void _BrowseAllExit( void *inContext )
5375 {
5376 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5377 SBRDomain * domain;
5378 SBRServiceType * type;
5379 SBRServiceInstance * instance;
5380 SBRIPAddress * ipaddr;
5381 char textBuf[ 512 ];
5382 #if( TARGET_OS_POSIX )
5383 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5384 #endif
5385
5386 dispatch_source_forget( &context->connectionTimer );
5387
5388 for( domain = context->results->domainList; domain; domain = domain->next )
5389 {
5390 FPrintF( stdout, "%s\n\n", domain->name );
5391
5392 for( type = domain->typeList; type; type = type->next )
5393 {
5394 const char * description;
5395 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5396
5397 description = ServiceTypeDescription( type->name );
5398 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5399 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5400
5401 for( instance = type->instanceList; instance; instance = instance->next )
5402 {
5403 char * dst = textBuf;
5404 char * const lim = &textBuf[ countof( textBuf ) ];
5405 char ifname[ IF_NAMESIZE + 1 ];
5406
5407 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5408 if( instance->ifIndex == 0 )
5409 {
5410 SNPrintF_Add( &dst, lim, "the Internet" );
5411 }
5412 else if( if_indextoname( instance->ifIndex, ifname ) )
5413 {
5414 NetTransportType netType;
5415
5416 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5417 SNPrintF_Add( &dst, lim, "%s (%s)",
5418 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5419 ifname );
5420 }
5421 else
5422 {
5423 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5424 }
5425 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5426 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5427
5428 if( instance->hostname )
5429 {
5430 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5431 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5432 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5433 }
5434 else
5435 {
5436 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5437 }
5438
5439 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5440 {
5441 BrowseAllConnection * conn;
5442 BrowseAllConnection ** connPtr;
5443
5444 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5445 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5446
5447 conn = NULL;
5448 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5449 {
5450 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5451 {
5452 if( ( conn->port == instance->port ) &&
5453 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5454 }
5455 if( conn )
5456 {
5457 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5458 *connPtr = conn->next;
5459 conn->context = NULL;
5460 AsyncConnection_Forget( &conn->asyncCnx );
5461 }
5462 }
5463
5464 if( conn )
5465 {
5466 if( conn->status == kNoErr )
5467 {
5468 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5469 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5470 }
5471 else
5472 {
5473 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5474 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5475 }
5476 _BrowseAllConnectionRelease( conn );
5477 }
5478 else
5479 {
5480 FPrintF( stdout, " (no connection attempted)\n" );
5481 }
5482 }
5483
5484 FPrintF( stdout, "\n" );
5485 if( instance->txtLen == 0 ) continue;
5486
5487 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5488 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5489 if( instance->txtLen > 1 )
5490 {
5491 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5492 }
5493 else
5494 {
5495 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5496 }
5497 FPrintF( stdout, "\n" );
5498 }
5499 FPrintF( stdout, "\n" );
5500 }
5501 }
5502
5503 _BrowseAllContextFree( context );
5504 Exit( NULL );
5505 }
5506
5507 //===========================================================================================================================
5508 // _IsServiceTypeTCP
5509 //===========================================================================================================================
5510
5511 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5512 {
5513 OSStatus err;
5514 const uint8_t * secondLabel;
5515 uint8_t name[ kDomainNameLengthMax ];
5516
5517 err = DomainNameFromString( name, inServiceType, NULL );
5518 if( !err )
5519 {
5520 secondLabel = DomainNameGetNextLabel( name );
5521 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5522 }
5523 return( false );
5524 }
5525
5526 //===========================================================================================================================
5527 // GetNameInfoCmd
5528 //===========================================================================================================================
5529
5530 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5531 {
5532 CaseFlagStringify( NI_NUMERICSCOPE ),
5533 CaseFlagStringify( NI_DGRAM ),
5534 CaseFlagStringify( NI_NUMERICSERV ),
5535 CaseFlagStringify( NI_NAMEREQD ),
5536 CaseFlagStringify( NI_NUMERICHOST ),
5537 CaseFlagStringify( NI_NOFQDN ),
5538 { 0, NULL }
5539 };
5540
5541 static void GetNameInfoCmd( void )
5542 {
5543 OSStatus err;
5544 sockaddr_ip sip;
5545 size_t sockAddrLen;
5546 unsigned int flags;
5547 const FlagStringPair * pair;
5548 struct timeval now;
5549 char host[ NI_MAXHOST ];
5550 char serv[ NI_MAXSERV ];
5551
5552 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5553 check_noerr( err );
5554 if( err )
5555 {
5556 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5557 goto exit;
5558 }
5559
5560 flags = 0;
5561 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5562 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5563 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5564 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5565 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5566 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5567
5568 // Print prologue.
5569
5570 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5571 FPrintF( stdout, "Flags: 0x%X < ", flags );
5572 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5573 {
5574 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5575 }
5576 FPrintF( stdout, ">\n" );
5577 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5578 FPrintF( stdout, "---\n" );
5579
5580 // Call getnameinfo().
5581
5582 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5583 (int) flags );
5584 gettimeofday( &now, NULL );
5585 if( err )
5586 {
5587 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5588 }
5589 else
5590 {
5591 FPrintF( stdout, "host: %s\n", host );
5592 FPrintF( stdout, "serv: %s\n", serv );
5593 }
5594 FPrintF( stdout, "---\n" );
5595 FPrintF( stdout, "End time: %{du:time}\n", &now );
5596
5597 exit:
5598 gExitCode = err ? 1 : 0;
5599 }
5600
5601 //===========================================================================================================================
5602 // GetAddrInfoStressCmd
5603 //===========================================================================================================================
5604
5605 typedef struct
5606 {
5607 DNSServiceRef mainRef;
5608 DNSServiceRef sdRef;
5609 DNSServiceFlags flags;
5610 unsigned int interfaceIndex;
5611 unsigned int connectionNumber;
5612 unsigned int requestCount;
5613 unsigned int requestCountMax;
5614 unsigned int requestCountLimit;
5615 unsigned int durationMinMs;
5616 unsigned int durationMaxMs;
5617
5618 } GAIStressContext;
5619
5620 static void GetAddrInfoStressEvent( void *inContext );
5621 static void DNSSD_API
5622 GetAddrInfoStressCallback(
5623 DNSServiceRef inSDRef,
5624 DNSServiceFlags inFlags,
5625 uint32_t inInterfaceIndex,
5626 DNSServiceErrorType inError,
5627 const char * inHostname,
5628 const struct sockaddr * inSockAddr,
5629 uint32_t inTTL,
5630 void * inContext );
5631
5632 static void GetAddrInfoStressCmd( void )
5633 {
5634 OSStatus err;
5635 GAIStressContext * context = NULL;
5636 int i;
5637 DNSServiceFlags flags;
5638 uint32_t ifIndex;
5639 char ifName[ kInterfaceNameBufLen ];
5640
5641 if( gGAIStress_TestDurationSecs < 0 )
5642 {
5643 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5644 err = kParamErr;
5645 goto exit;
5646 }
5647 if( gGAIStress_ConnectionCount <= 0 )
5648 {
5649 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5650 err = kParamErr;
5651 goto exit;
5652 }
5653 if( gGAIStress_DurationMinMs <= 0 )
5654 {
5655 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5656 err = kParamErr;
5657 goto exit;
5658 }
5659 if( gGAIStress_DurationMaxMs <= 0 )
5660 {
5661 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5662 err = kParamErr;
5663 goto exit;
5664 }
5665 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5666 {
5667 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5668 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5669 err = kParamErr;
5670 goto exit;
5671 }
5672 if( gGAIStress_RequestCountMax <= 0 )
5673 {
5674 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5675 err = kParamErr;
5676 goto exit;
5677 }
5678
5679 // Set flags.
5680
5681 flags = GetDNSSDFlagsFromOpts();
5682
5683 // Set interface index.
5684
5685 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5686 require_noerr_quiet( err, exit );
5687
5688 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5689 {
5690 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5691 require_action( context, exit, err = kNoMemoryErr );
5692
5693 context->flags = flags;
5694 context->interfaceIndex = ifIndex;
5695 context->connectionNumber = (unsigned int)( i + 1 );
5696 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5697 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5698 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5699
5700 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5701 context = NULL;
5702 }
5703
5704 if( gGAIStress_TestDurationSecs > 0 )
5705 {
5706 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5707 }
5708
5709 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5710 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5711 FPrintF( stdout, "Test duration: " );
5712 if( gGAIStress_TestDurationSecs == 0 )
5713 {
5714 FPrintF( stdout, "∞\n" );
5715 }
5716 else
5717 {
5718 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5719 }
5720 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5721 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5722 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5723 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5724 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5725 FPrintF( stdout, "---\n" );
5726
5727 dispatch_main();
5728
5729 exit:
5730 FreeNullSafe( context );
5731 if( err ) exit( 1 );
5732 }
5733
5734 //===========================================================================================================================
5735 // GetAddrInfoStressEvent
5736 //===========================================================================================================================
5737
5738 #define kStressRandStrLen 5
5739
5740 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5741
5742 static void GetAddrInfoStressEvent( void *inContext )
5743 {
5744 GAIStressContext * const context = (GAIStressContext *) inContext;
5745 OSStatus err;
5746 DNSServiceRef sdRef;
5747 unsigned int nextMs;
5748 char randomStr[ kStressRandStrLen + 1 ];
5749 char hostname[ kStressRandStrLen + 4 + 1 ];
5750 Boolean isConnectionNew = false;
5751 static Boolean printedHeader = false;
5752
5753 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5754 {
5755 DNSServiceForget( &context->mainRef );
5756 context->sdRef = NULL;
5757 context->requestCount = 0;
5758 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5759
5760 err = DNSServiceCreateConnection( &context->mainRef );
5761 require_noerr( err, exit );
5762
5763 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5764 require_noerr( err, exit );
5765
5766 isConnectionNew = true;
5767 }
5768
5769 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5770 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5771
5772 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5773
5774 if( !printedHeader )
5775 {
5776 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5777 printedHeader = true;
5778 }
5779 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5780 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5781
5782 DNSServiceForget( &context->sdRef );
5783 sdRef = context->mainRef;
5784 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5785 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5786 require_noerr( err, exit );
5787 context->sdRef = sdRef;
5788
5789 context->requestCount++;
5790
5791 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5792
5793 exit:
5794 if( err ) exit( 1 );
5795 }
5796
5797 //===========================================================================================================================
5798 // GetAddrInfoStressCallback
5799 //===========================================================================================================================
5800
5801 static void DNSSD_API
5802 GetAddrInfoStressCallback(
5803 DNSServiceRef inSDRef,
5804 DNSServiceFlags inFlags,
5805 uint32_t inInterfaceIndex,
5806 DNSServiceErrorType inError,
5807 const char * inHostname,
5808 const struct sockaddr * inSockAddr,
5809 uint32_t inTTL,
5810 void * inContext )
5811 {
5812 Unused( inSDRef );
5813 Unused( inFlags );
5814 Unused( inInterfaceIndex );
5815 Unused( inError );
5816 Unused( inHostname );
5817 Unused( inSockAddr );
5818 Unused( inTTL );
5819 Unused( inContext );
5820 }
5821
5822 //===========================================================================================================================
5823 // DNSQueryCmd
5824 //===========================================================================================================================
5825
5826 typedef struct
5827 {
5828 sockaddr_ip serverAddr;
5829 uint64_t sendTicks;
5830 uint8_t * msgPtr;
5831 size_t msgLen;
5832 size_t msgOffset;
5833 const char * name;
5834 dispatch_source_t readSource;
5835 SocketRef sock;
5836 int timeLimitSecs;
5837 uint16_t queryID;
5838 uint16_t type;
5839 Boolean haveTCPLen;
5840 Boolean useTCP;
5841 Boolean printRawRData; // True if RDATA results are not to be formatted.
5842 uint8_t msgBuf[ 512 ];
5843
5844 } DNSQueryContext;
5845
5846 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5847 static void DNSQueryReadHandler( void *inContext );
5848 static void DNSQueryCancelHandler( void *inContext );
5849
5850 static void DNSQueryCmd( void )
5851 {
5852 OSStatus err;
5853 DNSQueryContext * context = NULL;
5854 uint8_t * msgPtr;
5855 size_t msgLen, sendLen;
5856
5857 // Check command parameters.
5858
5859 if( gDNSQuery_TimeLimitSecs < -1 )
5860 {
5861 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5862 err = kParamErr;
5863 goto exit;
5864 }
5865 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5866 {
5867 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5868 err = kParamErr;
5869 goto exit;
5870 }
5871
5872 // Create context.
5873
5874 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5875 require_action( context, exit, err = kNoMemoryErr );
5876
5877 context->name = gDNSQuery_Name;
5878 context->sock = kInvalidSocketRef;
5879 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5880 context->queryID = (uint16_t) Random32();
5881 context->useTCP = gDNSQuery_UseTCP ? true : false;
5882 context->printRawRData = gDNSQuery_RawRData ? true : false;
5883
5884 #if( TARGET_OS_DARWIN )
5885 if( gDNSQuery_Server )
5886 #endif
5887 {
5888 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5889 require_noerr( err, exit );
5890 }
5891 #if( TARGET_OS_DARWIN )
5892 else
5893 {
5894 err = GetDefaultDNSServer( &context->serverAddr );
5895 require_noerr( err, exit );
5896 }
5897 #endif
5898 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5899
5900 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5901 require_noerr( err, exit );
5902
5903 // Write query message.
5904
5905 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5906
5907 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5908 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5909 kDNSServiceClass_IN, &msgLen );
5910 require_noerr( err, exit );
5911 check( msgLen <= UINT16_MAX );
5912
5913 if( context->useTCP )
5914 {
5915 WriteBig16( context->msgBuf, msgLen );
5916 sendLen = 2 + msgLen;
5917 }
5918 else
5919 {
5920 sendLen = msgLen;
5921 }
5922
5923 DNSQueryPrintPrologue( context );
5924
5925 if( gDNSQuery_Verbose )
5926 {
5927 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5928 FPrintF( stdout, "---\n" );
5929 }
5930
5931 if( context->useTCP )
5932 {
5933 // Create TCP socket.
5934
5935 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5936 err = map_socket_creation_errno( context->sock );
5937 require_noerr( err, exit );
5938
5939 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5940 require_noerr( err, exit );
5941 }
5942 else
5943 {
5944 // Create UDP socket.
5945
5946 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5947 require_noerr( err, exit );
5948 }
5949
5950 context->sendTicks = UpTicks();
5951 err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5952 require_noerr( err, exit );
5953
5954 if( context->timeLimitSecs == 0 ) goto exit;
5955
5956 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5957 &context->readSource );
5958 require_noerr( err, exit );
5959 dispatch_resume( context->readSource );
5960
5961 if( context->timeLimitSecs > 0 )
5962 {
5963 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5964 Exit );
5965 }
5966 dispatch_main();
5967
5968 exit:
5969 if( context )
5970 {
5971 dispatch_source_forget( &context->readSource );
5972 ForgetSocket( &context->sock );
5973 free( context );
5974 }
5975 if( err ) exit( 1 );
5976 }
5977
5978 //===========================================================================================================================
5979 // DNSQueryPrintPrologue
5980 //===========================================================================================================================
5981
5982 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5983 {
5984 const int timeLimitSecs = inContext->timeLimitSecs;
5985
5986 FPrintF( stdout, "Name: %s\n", inContext->name );
5987 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5988 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5989 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5990 FPrintF( stdout, "Time limit: " );
5991 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5992 else FPrintF( stdout, "∞\n" );
5993 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5994 FPrintF( stdout, "---\n" );
5995 }
5996
5997 //===========================================================================================================================
5998 // DNSQueryReadHandler
5999 //===========================================================================================================================
6000
6001 static void DNSQueryReadHandler( void *inContext )
6002 {
6003 OSStatus err;
6004 struct timeval now;
6005 const uint64_t nowTicks = UpTicks();
6006 DNSQueryContext * const context = (DNSQueryContext *) inContext;
6007
6008 gettimeofday( &now, NULL );
6009
6010 if( context->useTCP )
6011 {
6012 if( !context->haveTCPLen )
6013 {
6014 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
6015 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6016 require_noerr( err, exit );
6017
6018 context->msgOffset = 0;
6019 context->msgLen = ReadBig16( context->msgBuf );
6020 context->haveTCPLen = true;
6021 if( context->msgLen <= sizeof( context->msgBuf ) )
6022 {
6023 context->msgPtr = context->msgBuf;
6024 }
6025 else
6026 {
6027 context->msgPtr = (uint8_t *) malloc( context->msgLen );
6028 require_action( context->msgPtr, exit, err = kNoMemoryErr );
6029 }
6030 }
6031
6032 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
6033 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6034 require_noerr( err, exit );
6035 context->msgOffset = 0;
6036 context->haveTCPLen = false;
6037 }
6038 else
6039 {
6040 sockaddr_ip fromAddr;
6041
6042 context->msgPtr = context->msgBuf;
6043 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
6044 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6045 require_noerr( err, exit );
6046
6047 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6048 }
6049
6050 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6051 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6052 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6053 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6054 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
6055
6056 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
6057 {
6058 Exit( kExitReason_ReceivedResponse );
6059 }
6060
6061 exit:
6062 if( err ) dispatch_source_forget( &context->readSource );
6063 }
6064
6065 //===========================================================================================================================
6066 // DNSQueryCancelHandler
6067 //===========================================================================================================================
6068
6069 static void DNSQueryCancelHandler( void *inContext )
6070 {
6071 DNSQueryContext * const context = (DNSQueryContext *) inContext;
6072
6073 check( !context->readSource );
6074 ForgetSocket( &context->sock );
6075 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
6076 free( context );
6077 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
6078 }
6079
6080 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
6081 //===========================================================================================================================
6082 // DNSCryptCmd
6083 //===========================================================================================================================
6084
6085 #define kDNSCryptPort 443
6086
6087 #define kDNSCryptMinPadLength 8
6088 #define kDNSCryptMaxPadLength 256
6089 #define kDNSCryptBlockSize 64
6090 #define kDNSCryptCertMinimumLength 124
6091 #define kDNSCryptClientMagicLength 8
6092 #define kDNSCryptResolverMagicLength 8
6093 #define kDNSCryptHalfNonceLength 12
6094 #define kDNSCryptCertMagicLength 4
6095
6096 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
6097
6098 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
6099 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
6100 {
6101 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
6102 };
6103
6104 typedef struct
6105 {
6106 uint8_t certMagic[ kDNSCryptCertMagicLength ];
6107 uint8_t esVersion[ 2 ];
6108 uint8_t minorVersion[ 2 ];
6109 uint8_t signature[ crypto_sign_BYTES ];
6110 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
6111 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6112 uint8_t serial[ 4 ];
6113 uint8_t startTime[ 4 ];
6114 uint8_t endTime[ 4 ];
6115 uint8_t extensions[ 1 ]; // Variably-sized extension data.
6116
6117 } DNSCryptCert;
6118
6119 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
6120
6121 typedef struct
6122 {
6123 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6124 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6125 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6126 uint8_t poly1305MAC[ 16 ];
6127
6128 } DNSCryptQueryHeader;
6129
6130 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
6131 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
6132 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
6133 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
6134
6135 typedef struct
6136 {
6137 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
6138 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6139 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
6140 uint8_t poly1305MAC[ 16 ];
6141
6142 } DNSCryptResponseHeader;
6143
6144 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
6145 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
6146 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
6147 sizeof( DNSCryptResponseHeader ) );
6148
6149 typedef struct
6150 {
6151 sockaddr_ip serverAddr;
6152 uint64_t sendTicks;
6153 const char * providerName;
6154 const char * qname;
6155 const uint8_t * certPtr;
6156 size_t certLen;
6157 dispatch_source_t readSource;
6158 size_t msgLen;
6159 int timeLimitSecs;
6160 uint16_t queryID;
6161 uint16_t qtype;
6162 Boolean printRawRData;
6163 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
6164 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
6165 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6166 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
6167 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6168 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6169 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
6170 uint8_t msgBuf[ 512 ];
6171
6172 } DNSCryptContext;
6173
6174 static void DNSCryptReceiveCertHandler( void *inContext );
6175 static void DNSCryptReceiveResponseHandler( void *inContext );
6176 static void DNSCryptProceed( void *inContext );
6177 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
6178 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
6179 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
6180 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
6181
6182 static void DNSCryptCmd( void )
6183 {
6184 OSStatus err;
6185 DNSCryptContext * context = NULL;
6186 size_t writtenBytes;
6187 size_t totalBytes;
6188 SocketContext * sockCtx;
6189 SocketRef sock = kInvalidSocketRef;
6190 const char * ptr;
6191
6192 // Check command parameters.
6193
6194 if( gDNSCrypt_TimeLimitSecs < -1 )
6195 {
6196 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
6197 err = kParamErr;
6198 goto exit;
6199 }
6200
6201 // Create context.
6202
6203 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
6204 require_action( context, exit, err = kNoMemoryErr );
6205
6206 context->providerName = gDNSCrypt_ProviderName;
6207 context->qname = gDNSCrypt_Name;
6208 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
6209 context->printRawRData = gDNSCrypt_RawRData ? true : false;
6210
6211 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
6212 require_noerr( err, exit );
6213
6214 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
6215 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
6216 if( err || ( *ptr != '\0' ) )
6217 {
6218 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
6219 goto exit;
6220 }
6221 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
6222 {
6223 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6224 totalBytes, sizeof( context->serverPublicSignKey ) );
6225 err = kSizeErr;
6226 goto exit;
6227 }
6228 check( writtenBytes == totalBytes );
6229
6230 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6231 require_noerr( err, exit );
6232 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
6233
6234 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
6235 require_noerr( err, exit );
6236
6237 // Write query message.
6238
6239 context->queryID = (uint16_t) Random32();
6240 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6241 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6242 require_noerr( err, exit );
6243
6244 // Create UDP socket.
6245
6246 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6247 require_noerr( err, exit );
6248
6249 // Send DNS query.
6250
6251 context->sendTicks = UpTicks();
6252 err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6253 require_noerr( err, exit );
6254
6255 err = SocketContextCreate( sock, context, &sockCtx );
6256 require_noerr( err, exit );
6257 sock = kInvalidSocketRef;
6258
6259 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6260 &context->readSource );
6261 if( err ) ForgetSocketContext( &sockCtx );
6262 require_noerr( err, exit );
6263
6264 dispatch_resume( context->readSource );
6265
6266 if( context->timeLimitSecs > 0 )
6267 {
6268 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6269 Exit );
6270 }
6271 dispatch_main();
6272
6273 exit:
6274 if( context ) free( context );
6275 ForgetSocket( &sock );
6276 if( err ) exit( 1 );
6277 }
6278
6279 //===========================================================================================================================
6280 // DNSCryptReceiveCertHandler
6281 //===========================================================================================================================
6282
6283 static void DNSCryptReceiveCertHandler( void *inContext )
6284 {
6285 OSStatus err;
6286 struct timeval now;
6287 const uint64_t nowTicks = UpTicks();
6288 SocketContext * const sockCtx = (SocketContext *) inContext;
6289 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6290 const DNSHeader * hdr;
6291 sockaddr_ip fromAddr;
6292 const uint8_t * ptr;
6293 const uint8_t * txtPtr;
6294 size_t txtLen;
6295 unsigned int answerCount, i;
6296 uint8_t targetName[ kDomainNameLengthMax ];
6297
6298 gettimeofday( &now, NULL );
6299
6300 dispatch_source_forget( &context->readSource );
6301
6302 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6303 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6304 require_noerr( err, exit );
6305 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6306
6307 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6308 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6309 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6310 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6311 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6312
6313 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6314
6315 hdr = (DNSHeader *) context->msgBuf;
6316 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6317
6318 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6319 require_noerr( err, exit );
6320
6321 err = DomainNameFromString( targetName, context->providerName, NULL );
6322 require_noerr( err, exit );
6323
6324 answerCount = DNSHeaderGetAnswerCount( hdr );
6325 for( i = 0; i < answerCount; ++i )
6326 {
6327 uint16_t type;
6328 uint16_t class;
6329 uint8_t name[ kDomainNameLengthMax ];
6330
6331 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6332 &ptr );
6333 require_noerr( err, exit );
6334
6335 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6336 {
6337 break;
6338 }
6339 }
6340
6341 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6342 {
6343 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6344 err = kSizeErr;
6345 goto exit;
6346 }
6347 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6348 {
6349 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6350 err = kSizeErr;
6351 goto exit;
6352 }
6353
6354 context->certLen = txtPtr[ 0 ];
6355 context->certPtr = &txtPtr[ 1 ];
6356
6357 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6358
6359 exit:
6360 if( err ) Exit( NULL );
6361 }
6362
6363 //===========================================================================================================================
6364 // DNSCryptReceiveResponseHandler
6365 //===========================================================================================================================
6366
6367 static void DNSCryptReceiveResponseHandler( void *inContext )
6368 {
6369 OSStatus err;
6370 struct timeval now;
6371 const uint64_t nowTicks = UpTicks();
6372 SocketContext * const sockCtx = (SocketContext *) inContext;
6373 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6374 sockaddr_ip fromAddr;
6375 DNSCryptResponseHeader * hdr;
6376 const uint8_t * end;
6377 uint8_t * ciphertext;
6378 uint8_t * plaintext;
6379 const uint8_t * response;
6380 uint8_t nonce[ crypto_box_NONCEBYTES ];
6381
6382 gettimeofday( &now, NULL );
6383
6384 dispatch_source_forget( &context->readSource );
6385
6386 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6387 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6388 require_noerr( err, exit );
6389 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6390
6391 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6392 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6393 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6394 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6395
6396 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6397 {
6398 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6399 err = kSizeErr;
6400 goto exit;
6401 }
6402
6403 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6404
6405 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6406 {
6407 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6408 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6409 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6410 err = kValueErr;
6411 goto exit;
6412 }
6413
6414 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6415 {
6416 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6417 err = kValueErr;
6418 goto exit;
6419 }
6420
6421 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6422
6423 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6424 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6425
6426 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6427 check( plaintext == ciphertext );
6428
6429 end = context->msgBuf + context->msgLen;
6430
6431 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6432 require_noerr( err, exit );
6433
6434 response = plaintext + crypto_box_ZEROBYTES;
6435 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6436 Exit( kExitReason_ReceivedResponse );
6437
6438 exit:
6439 if( err ) Exit( NULL );
6440 }
6441
6442 //===========================================================================================================================
6443 // DNSCryptProceed
6444 //===========================================================================================================================
6445
6446 static void DNSCryptProceed( void *inContext )
6447 {
6448 OSStatus err;
6449 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6450
6451 err = DNSCryptProcessCert( context );
6452 require_noerr_quiet( err, exit );
6453
6454 err = DNSCryptBuildQuery( context );
6455 require_noerr_quiet( err, exit );
6456
6457 err = DNSCryptSendQuery( context );
6458 require_noerr_quiet( err, exit );
6459
6460 exit:
6461 if( err ) Exit( NULL );
6462 }
6463
6464 //===========================================================================================================================
6465 // DNSCryptProcessCert
6466 //===========================================================================================================================
6467
6468 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6469 {
6470 OSStatus err;
6471 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6472 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6473 struct timeval now;
6474 time_t startTimeSecs, endTimeSecs;
6475 size_t signedLen;
6476 uint8_t * tempBuf;
6477 unsigned long long tempLen;
6478
6479 DNSCryptPrintCertificate( cert, inContext->certLen );
6480
6481 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6482 {
6483 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6484 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6485 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6486 err = kValueErr;
6487 goto exit;
6488 }
6489
6490 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6491 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6492
6493 gettimeofday( &now, NULL );
6494 if( now.tv_sec < startTimeSecs )
6495 {
6496 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6497 err = kDateErr;
6498 goto exit;
6499 }
6500 if( now.tv_sec >= endTimeSecs )
6501 {
6502 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6503 err = kDateErr;
6504 goto exit;
6505 }
6506
6507 signedLen = (size_t)( certEnd - cert->signature );
6508 tempBuf = (uint8_t *) malloc( signedLen );
6509 require_action( tempBuf, exit, err = kNoMemoryErr );
6510 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6511 free( tempBuf );
6512 if( err )
6513 {
6514 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6515 err = kAuthenticationErr;
6516 goto exit;
6517 }
6518
6519 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6520 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6521
6522 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6523 require_noerr( err, exit );
6524
6525 inContext->certPtr = NULL;
6526 inContext->certLen = 0;
6527 inContext->msgLen = 0;
6528
6529 exit:
6530 return( err );
6531 }
6532
6533 //===========================================================================================================================
6534 // DNSCryptBuildQuery
6535 //===========================================================================================================================
6536
6537 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6538
6539 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6540 {
6541 OSStatus err;
6542 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6543 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6544 size_t queryLen;
6545 size_t paddedQueryLen;
6546 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6547 const uint8_t * padLimit;
6548 uint8_t nonce[ crypto_box_NONCEBYTES ];
6549
6550 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6551
6552 inContext->queryID = (uint16_t) Random32();
6553 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6554 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6555 require_noerr( err, exit );
6556
6557 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6558 if( padLimit > msgLimit ) padLimit = msgLimit;
6559
6560 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6561 require_noerr( err, exit );
6562
6563 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6564 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6565 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6566 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6567
6568 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6569 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6570 require_noerr( err, exit );
6571
6572 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6573 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6574 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6575
6576 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6577
6578 exit:
6579 return( err );
6580 }
6581
6582 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6583 {
6584 OSStatus err;
6585 size_t paddedLen;
6586
6587 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6588
6589 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6590 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6591 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6592 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6593
6594 inMsgPtr[ inMsgLen ] = 0x80;
6595 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6596
6597 if( outPaddedLen ) *outPaddedLen = paddedLen;
6598 err = kNoErr;
6599
6600 exit:
6601 return( err );
6602 }
6603
6604 //===========================================================================================================================
6605 // DNSCryptSendQuery
6606 //===========================================================================================================================
6607
6608 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6609 {
6610 OSStatus err;
6611 SocketContext * sockCtx;
6612 SocketRef sock = kInvalidSocketRef;
6613
6614 check( inContext->msgLen > 0 );
6615 check( !inContext->readSource );
6616
6617 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6618 require_noerr( err, exit );
6619
6620 inContext->sendTicks = UpTicks();
6621 err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6622 require_noerr( err, exit );
6623
6624 err = SocketContextCreate( sock, inContext, &sockCtx );
6625 require_noerr( err, exit );
6626 sock = kInvalidSocketRef;
6627
6628 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6629 &inContext->readSource );
6630 if( err ) ForgetSocketContext( &sockCtx );
6631 require_noerr( err, exit );
6632
6633 dispatch_resume( inContext->readSource );
6634
6635 exit:
6636 ForgetSocket( &sock );
6637 return( err );
6638 }
6639
6640 //===========================================================================================================================
6641 // DNSCryptPrintCertificate
6642 //===========================================================================================================================
6643
6644 #define kCertTimeStrBufLen 32
6645
6646 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6647
6648 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6649 {
6650 time_t startTime, endTime;
6651 int extLen;
6652 char timeBuf[ kCertTimeStrBufLen ];
6653
6654 check( inLen >= kDNSCryptCertMinimumLength );
6655
6656 startTime = (time_t) ReadBig32( inCert->startTime );
6657 endTime = (time_t) ReadBig32( inCert->endTime );
6658
6659 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6660 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6661 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6662 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6663 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6664 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6665 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6666 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6667 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6668 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6669 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6670
6671 if( inLen > kDNSCryptCertMinimumLength )
6672 {
6673 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6674 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6675 }
6676 FPrintF( stdout, "\n" );
6677 }
6678
6679 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6680 {
6681 struct tm * tm;
6682
6683 tm = localtime( &inTime );
6684 if( !tm )
6685 {
6686 dlogassert( "localtime() returned a NULL pointer.\n" );
6687 *inBuffer = '\0';
6688 }
6689 else
6690 {
6691 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6692 }
6693
6694 return( inBuffer );
6695 }
6696
6697 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6698
6699 //===========================================================================================================================
6700 // MDNSQueryCmd
6701 //===========================================================================================================================
6702
6703 typedef struct
6704 {
6705 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6706 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6707 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6708 int localPort; // The port number to which the sockets are bound.
6709 int receiveSecs; // After send, the amount of time to spend receiving.
6710 uint32_t ifIndex; // Index of the interface over which to send the query.
6711 uint16_t qtype; // The type (QTYPE) of the record being queried.
6712 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6713 Boolean allResponses; // True if all mDNS messages received should be printed.
6714 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6715 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6716 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6717 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6718 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6719 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
6720
6721 } MDNSQueryContext;
6722
6723 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6724 static void MDNSQueryReadHandler( void *inContext );
6725
6726 static void MDNSQueryCmd( void )
6727 {
6728 OSStatus err;
6729 MDNSQueryContext * context;
6730 SocketRef sockV4 = kInvalidSocketRef;
6731 SocketRef sockV6 = kInvalidSocketRef;
6732 ssize_t n;
6733 const char * ifname;
6734 size_t msgLen;
6735 unsigned int sendCount;
6736
6737 // Check command parameters.
6738
6739 if( gMDNSQuery_ReceiveSecs < -1 )
6740 {
6741 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6742 err = kParamErr;
6743 goto exit;
6744 }
6745
6746 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6747 require_action( context, exit, err = kNoMemoryErr );
6748
6749 context->qnameStr = gMDNSQuery_Name;
6750 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6751 context->isQU = gMDNSQuery_IsQU ? true : false;
6752 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6753 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6754 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6755 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6756
6757 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6758 require_noerr_quiet( err, exit );
6759
6760 ifname = if_indextoname( context->ifIndex, context->ifName );
6761 require_action( ifname, exit, err = kNameErr );
6762
6763 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6764 require_noerr( err, exit );
6765
6766 // Set up IPv4 socket.
6767
6768 if( context->useIPv4 )
6769 {
6770 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6771 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6772 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
6773 require_noerr( err, exit );
6774 }
6775
6776 // Set up IPv6 socket.
6777
6778 if( context->useIPv6 )
6779 {
6780 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6781 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6782 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
6783 require_noerr( err, exit );
6784 }
6785
6786 // Craft mDNS query message.
6787
6788 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6789 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6790 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6791 require_noerr( err, exit );
6792
6793 // Print prologue.
6794
6795 MDNSQueryPrintPrologue( context );
6796
6797 // Send mDNS query message.
6798
6799 sendCount = 0;
6800 if( IsValidSocket( sockV4 ) )
6801 {
6802 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
6803
6804 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
6805 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6806 if( err )
6807 {
6808 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6809 ForgetSocket( &sockV4 );
6810 }
6811 else
6812 {
6813 ++sendCount;
6814 }
6815 }
6816 if( IsValidSocket( sockV6 ) )
6817 {
6818 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
6819
6820 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
6821 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6822 if( err )
6823 {
6824 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6825 ForgetSocket( &sockV6 );
6826 }
6827 else
6828 {
6829 ++sendCount;
6830 }
6831 }
6832 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6833
6834 // If there's no wait period after the send, then exit.
6835
6836 if( context->receiveSecs == 0 ) goto exit;
6837
6838 // Create dispatch read sources for socket(s).
6839
6840 if( IsValidSocket( sockV4 ) )
6841 {
6842 SocketContext * sockCtx;
6843
6844 err = SocketContextCreate( sockV4, context, &sockCtx );
6845 require_noerr( err, exit );
6846 sockV4 = kInvalidSocketRef;
6847
6848 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6849 &context->readSourceV4 );
6850 if( err ) ForgetSocketContext( &sockCtx );
6851 require_noerr( err, exit );
6852
6853 dispatch_resume( context->readSourceV4 );
6854 }
6855
6856 if( IsValidSocket( sockV6 ) )
6857 {
6858 SocketContext * sockCtx;
6859
6860 err = SocketContextCreate( sockV6, context, &sockCtx );
6861 require_noerr( err, exit );
6862 sockV6 = kInvalidSocketRef;
6863
6864 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6865 &context->readSourceV6 );
6866 if( err ) ForgetSocketContext( &sockCtx );
6867 require_noerr( err, exit );
6868
6869 dispatch_resume( context->readSourceV6 );
6870 }
6871
6872 if( context->receiveSecs > 0 )
6873 {
6874 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6875 Exit );
6876 }
6877 dispatch_main();
6878
6879 exit:
6880 ForgetSocket( &sockV4 );
6881 ForgetSocket( &sockV6 );
6882 if( err ) exit( 1 );
6883 }
6884
6885 //===========================================================================================================================
6886 // MDNSColliderCmd
6887 //===========================================================================================================================
6888
6889 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
6890
6891 static void MDNSColliderCmd( void )
6892 {
6893 OSStatus err;
6894 MDNSColliderRef collider = NULL;
6895 uint8_t * rdataPtr = NULL;
6896 size_t rdataLen = 0;
6897 const char * ifname;
6898 uint32_t ifIndex;
6899 MDNSColliderProtocols protocols;
6900 uint16_t type;
6901 char ifName[ IF_NAMESIZE + 1 ];
6902 uint8_t name[ kDomainNameLengthMax ];
6903
6904 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6905 require_noerr_quiet( err, exit );
6906
6907 ifname = if_indextoname( ifIndex, ifName );
6908 if( !ifname )
6909 {
6910 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
6911 err = kNameErr;
6912 goto exit;
6913 }
6914
6915 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
6916 if( err )
6917 {
6918 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
6919 goto exit;
6920 }
6921
6922 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
6923 require_noerr_quiet( err, exit );
6924
6925 if( gMDNSCollider_RecordData )
6926 {
6927 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
6928 require_noerr_quiet( err, exit );
6929 }
6930
6931 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
6932 require_noerr( err, exit );
6933
6934 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
6935 if( err )
6936 {
6937 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
6938 goto exit;
6939 }
6940
6941 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
6942 require_noerr( err, exit );
6943 ForgetMem( &rdataPtr );
6944
6945 protocols = kMDNSColliderProtocol_None;
6946 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
6947 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
6948 MDNSColliderSetProtocols( collider, protocols );
6949 MDNSColliderSetInterfaceIndex( collider, ifIndex );
6950 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
6951
6952 err = MDNSColliderStart( collider );
6953 require_noerr( err, exit );
6954
6955 dispatch_main();
6956
6957 exit:
6958 FreeNullSafe( rdataPtr );
6959 CFReleaseNullSafe( collider );
6960 if( err ) exit( 1 );
6961 }
6962
6963 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
6964 {
6965 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
6966
6967 CFRelease( collider );
6968 exit( inError ? 1 : 0 );
6969 }
6970
6971 //===========================================================================================================================
6972 // MDNSQueryPrintPrologue
6973 //===========================================================================================================================
6974
6975 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6976 {
6977 const int receiveSecs = inContext->receiveSecs;
6978
6979 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6980 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6981 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6982 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6983 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6984 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6985 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6986 FPrintF( stdout, "Receive duration: " );
6987 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6988 else FPrintF( stdout, "∞\n" );
6989 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6990 }
6991
6992 //===========================================================================================================================
6993 // MDNSQueryReadHandler
6994 //===========================================================================================================================
6995
6996 static void MDNSQueryReadHandler( void *inContext )
6997 {
6998 OSStatus err;
6999 struct timeval now;
7000 SocketContext * const sockCtx = (SocketContext *) inContext;
7001 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
7002 size_t msgLen;
7003 sockaddr_ip fromAddr;
7004 Boolean foundAnswer = false;
7005
7006 gettimeofday( &now, NULL );
7007
7008 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
7009 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
7010 require_noerr( err, exit );
7011
7012 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
7013 {
7014 const uint8_t * ptr;
7015 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
7016 unsigned int rrCount, i;
7017 uint16_t type, class;
7018 uint8_t name[ kDomainNameLengthMax ];
7019
7020 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
7021 require_noerr( err, exit );
7022
7023 if( context->qname[ 0 ] == 0 )
7024 {
7025 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
7026 require_noerr( err, exit );
7027 }
7028
7029 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
7030 for( i = 0; i < rrCount; ++i )
7031 {
7032 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
7033 require_noerr( err, exit );
7034
7035 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
7036 DomainNameEqual( name, context->qname ) )
7037 {
7038 foundAnswer = true;
7039 break;
7040 }
7041 }
7042 }
7043 if( context->allResponses || foundAnswer )
7044 {
7045 FPrintF( stdout, "---\n" );
7046 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
7047 FPrintF( stdout, "Source: %##a\n", &fromAddr );
7048 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
7049 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
7050 }
7051
7052 exit:
7053 if( err ) exit( 1 );
7054 }
7055
7056 //===========================================================================================================================
7057 // PIDToUUIDCmd
7058 //===========================================================================================================================
7059
7060 static void PIDToUUIDCmd( void )
7061 {
7062 OSStatus err;
7063 int n;
7064 struct proc_uniqidentifierinfo info;
7065
7066 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
7067 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
7068
7069 FPrintF( stdout, "%#U\n", info.p_uuid );
7070 err = kNoErr;
7071
7072 exit:
7073 if( err ) exit( 1 );
7074 }
7075
7076 //===========================================================================================================================
7077 // DNSServerCmd
7078 //===========================================================================================================================
7079
7080 typedef struct DNSServerPrivate * DNSServerRef;
7081
7082 typedef struct
7083 {
7084 DNSServerRef server; // Reference to the DNS server.
7085 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
7086 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
7087 const char * domainOverride; // If non-NULL, the server is to use this domain instead of "d.test.".
7088 #if( TARGET_OS_DARWIN )
7089 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
7090 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
7091 Boolean addedResolver; // True if system DNS settings contains a resolver entry for server.
7092 #endif
7093 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
7094
7095 } DNSServerCmdContext;
7096
7097 typedef enum
7098 {
7099 kDNSServerEvent_Started = 1,
7100 kDNSServerEvent_Stopped = 2
7101
7102 } DNSServerEventType;
7103
7104 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
7105
7106 CFTypeID DNSServerGetTypeID( void );
7107 static OSStatus
7108 DNSServerCreate(
7109 dispatch_queue_t inQueue,
7110 DNSServerEventHandler_f inEventHandler,
7111 void * inEventContext,
7112 unsigned int inResponseDelayMs,
7113 uint32_t inDefaultTTL,
7114 int inPort,
7115 Boolean inLoopbackOnly,
7116 const char * inDomain,
7117 Boolean inBadUDPMode,
7118 DNSServerRef * outServer );
7119 static void DNSServerStart( DNSServerRef inServer );
7120 static void DNSServerStop( DNSServerRef inServer );
7121
7122 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
7123
7124 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
7125 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
7126 static void DNSServerCmdSigIntHandler( void *inContext );
7127 static void DNSServerCmdSigTermHandler( void *inContext );
7128 #if( TARGET_OS_DARWIN )
7129 static void DNSServerCmdFollowedProcessHandler( void *inContext );
7130 #endif
7131
7132 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
7133 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
7134
7135 static void DNSServerCmd( void )
7136 {
7137 OSStatus err;
7138 DNSServerCmdContext * context = NULL;
7139
7140 if( gDNSServer_Foreground )
7141 {
7142 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
7143 }
7144
7145 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
7146 require_noerr_quiet( err, exit );
7147
7148 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
7149 require_noerr_quiet( err, exit );
7150
7151 err = CheckIntegerArgument( gDNSServer_Port, "port number", -UINT16_MAX, UINT16_MAX );
7152 require_noerr_quiet( err, exit );
7153
7154 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
7155 require_action( context, exit, err = kNoMemoryErr );
7156
7157 context->domainOverride = gDNSServer_DomainOverride;
7158 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
7159
7160 #if( TARGET_OS_DARWIN )
7161 if( gDNSServer_FollowPID )
7162 {
7163 context->followPID = _StringToPID( gDNSServer_FollowPID, &err );
7164 if( err || ( context->followPID < 0 ) )
7165 {
7166 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
7167 err = kParamErr;
7168 goto exit;
7169 }
7170
7171 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
7172 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
7173 require_noerr( err, exit );
7174 dispatch_resume( context->processMonitor );
7175 }
7176 else
7177 {
7178 context->followPID = -1;
7179 }
7180 #endif
7181
7182 signal( SIGINT, SIG_IGN );
7183 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), DNSServerCmdSigIntHandler, context,
7184 &context->sigIntSource );
7185 require_noerr( err, exit );
7186 dispatch_resume( context->sigIntSource );
7187
7188 signal( SIGTERM, SIG_IGN );
7189 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), DNSServerCmdSigTermHandler, context,
7190 &context->sigTermSource );
7191 require_noerr( err, exit );
7192 dispatch_resume( context->sigTermSource );
7193
7194 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context,
7195 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, gDNSServer_Port, context->loopbackOnly,
7196 context->domainOverride, gDNSServer_BadUDPMode ? true : false, &context->server );
7197 require_noerr( err, exit );
7198
7199 DNSServerStart( context->server );
7200 dispatch_main();
7201
7202 exit:
7203 FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
7204 if( context ) DNSServerCmdContextFree( context );
7205 if( err ) exit( 1 );
7206 }
7207
7208 //===========================================================================================================================
7209 // DNSServerCmdContextFree
7210 //===========================================================================================================================
7211
7212 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
7213 {
7214 ForgetCF( &inContext->server );
7215 dispatch_source_forget( &inContext->sigIntSource );
7216 dispatch_source_forget( &inContext->sigTermSource );
7217 #if( TARGET_OS_DARWIN )
7218 dispatch_source_forget( &inContext->processMonitor );
7219 #endif
7220 free( inContext );
7221 }
7222
7223 //===========================================================================================================================
7224 // DNSServerCmdEventHandler
7225 //===========================================================================================================================
7226
7227 #if( TARGET_OS_DARWIN )
7228 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort );
7229 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
7230 #endif
7231
7232 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext )
7233 {
7234 OSStatus err;
7235 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7236
7237 if( inType == kDNSServerEvent_Started )
7238 {
7239 #if( TARGET_OS_DARWIN )
7240 const int port = (int) inEventData;
7241
7242 err = _DNSServerCmdLoopbackResolverAdd( context->domainOverride ? context->domainOverride : "d.test.", port );
7243 if( err )
7244 {
7245 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7246 err );
7247 if( context->loopbackOnly ) ForgetDNSServer( &context->server );
7248 }
7249 else
7250 {
7251 context->addedResolver = true;
7252 }
7253 #endif
7254 }
7255 else if( inType == kDNSServerEvent_Stopped )
7256 {
7257 const OSStatus stopError = (OSStatus) inEventData;
7258
7259 if( stopError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", stopError );
7260
7261 err = kNoErr;
7262 #if( TARGET_OS_DARWIN )
7263 if( context->addedResolver )
7264 {
7265 err = _DNSServerCmdLoopbackResolverRemove();
7266 if( err )
7267 {
7268 ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7269 }
7270 else
7271 {
7272 context->addedResolver = false;
7273 }
7274 }
7275 else if( context->loopbackOnly )
7276 {
7277 err = kUnknownErr;
7278 }
7279 #endif
7280 DNSServerCmdContextFree( context );
7281 exit( ( stopError || err ) ? 1 : 0 );
7282 }
7283 }
7284
7285 #if( TARGET_OS_DARWIN )
7286 //===========================================================================================================================
7287 // _DNSServerCmdLoopbackResolverAdd
7288 //===========================================================================================================================
7289
7290 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort )
7291 {
7292 OSStatus err;
7293 SCDynamicStoreRef store;
7294 CFPropertyListRef plist = NULL;
7295 CFStringRef key = NULL;
7296 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7297 Boolean success;
7298
7299 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7300 err = map_scerror( store );
7301 require_noerr( err, exit );
7302
7303 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7304 "{"
7305 "%kO="
7306 "["
7307 "%s"
7308 "]"
7309 "%kO="
7310 "["
7311 "%.4a"
7312 "%.16a"
7313 "]"
7314 "%kO=%i"
7315 "%kO=%O"
7316 "%kO=%O"
7317 "}",
7318 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7319 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr,
7320 kSCPropNetDNSServerPort, inPort,
7321 kSCPropInterfaceName, CFSTR( "lo0" ),
7322 kSCPropNetDNSConfirmedServiceID, CFSTR( "com.apple.dnssdutil.server" ) );
7323 require_noerr( err, exit );
7324
7325 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7326 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7327 require_action( key, exit, err = kUnknownErr );
7328
7329 success = SCDynamicStoreSetValue( store, key, plist );
7330 require_action( success, exit, err = kUnknownErr );
7331
7332 exit:
7333 CFReleaseNullSafe( store );
7334 CFReleaseNullSafe( plist );
7335 CFReleaseNullSafe( key );
7336 return( err );
7337 }
7338
7339 //===========================================================================================================================
7340 // _DNSServerCmdLoopbackResolverRemove
7341 //===========================================================================================================================
7342
7343 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7344 {
7345 OSStatus err;
7346 SCDynamicStoreRef store;
7347 CFStringRef key = NULL;
7348 Boolean success;
7349
7350 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7351 err = map_scerror( store );
7352 require_noerr( err, exit );
7353
7354 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7355 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7356 require_action( key, exit, err = kUnknownErr );
7357
7358 success = SCDynamicStoreRemoveValue( store, key );
7359 require_action( success, exit, err = kUnknownErr );
7360
7361 exit:
7362 CFReleaseNullSafe( store );
7363 CFReleaseNullSafe( key );
7364 return( err );
7365 }
7366 #endif
7367
7368 //===========================================================================================================================
7369 // DNSServerCmdSigIntHandler
7370 //===========================================================================================================================
7371
7372 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal );
7373
7374 static void DNSServerCmdSigIntHandler( void *inContext )
7375 {
7376 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGINT );
7377 }
7378
7379 //===========================================================================================================================
7380 // DNSServerCmdSigTermHandler
7381 //===========================================================================================================================
7382
7383 static void DNSServerCmdSigTermHandler( void *inContext )
7384 {
7385 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGTERM );
7386 }
7387
7388 #if( TARGET_OS_DARWIN )
7389 //===========================================================================================================================
7390 // DNSServerCmdFollowedProcessHandler
7391 //===========================================================================================================================
7392
7393 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7394 {
7395 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7396
7397 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( context, 0 );
7398 }
7399 #endif
7400
7401 //===========================================================================================================================
7402 // _DNSServerCmdExternalExit
7403 //===========================================================================================================================
7404
7405 #define SignalNumberToString( X ) ( \
7406 ( (X) == SIGINT ) ? "SIGINT" : \
7407 ( (X) == SIGTERM ) ? "SIGTERM" : \
7408 "???" )
7409
7410 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal )
7411 {
7412 dispatch_source_forget( &inContext->sigIntSource );
7413 dispatch_source_forget( &inContext->sigTermSource );
7414 #if( TARGET_OS_DARWIN )
7415 dispatch_source_forget( &inContext->processMonitor );
7416
7417 if( inSignal == 0 )
7418 {
7419 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7420 }
7421 else
7422 #endif
7423 {
7424 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7425 }
7426
7427 ForgetDNSServer( &inContext->server );
7428 }
7429
7430 //===========================================================================================================================
7431 // DNSServerCreate
7432 //===========================================================================================================================
7433
7434 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7435
7436 typedef struct DNSDelayedResponse DNSDelayedResponse;
7437 struct DNSDelayedResponse
7438 {
7439 DNSDelayedResponse * next;
7440 sockaddr_ip destAddr;
7441 uint64_t targetTicks;
7442 uint8_t * msgPtr;
7443 size_t msgLen;
7444 };
7445
7446 struct DNSServerPrivate
7447 {
7448 CFRuntimeBase base; // CF object base.
7449 uint8_t * domain; // Parent domain of server's resource records.
7450 dispatch_queue_t queue; // Queue for DNS server's events.
7451 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7452 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7453 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7454 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7455 SocketRef sockUDPv4;
7456 SocketRef sockUDPv6;
7457 DNSServerEventHandler_f eventHandler;
7458 void * eventContext;
7459 DNSDelayedResponse * responseList;
7460 dispatch_source_t responseTimer;
7461 unsigned int responseDelayMs;
7462 uint32_t defaultTTL;
7463 uint32_t serial; // Serial number for SOA record.
7464 int port; // Port to use for receiving and sending DNS messages.
7465 OSStatus stopError;
7466 Boolean stopped;
7467 Boolean loopbackOnly;
7468 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
7469 };
7470
7471 static void _DNSServerUDPReadHandler( void *inContext );
7472 static void _DNSServerTCPReadHandler( void *inContext );
7473 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse );
7474 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList );
7475
7476 CF_CLASS_DEFINE( DNSServer );
7477
7478 static OSStatus
7479 DNSServerCreate(
7480 dispatch_queue_t inQueue,
7481 DNSServerEventHandler_f inEventHandler,
7482 void * inEventContext,
7483 unsigned int inResponseDelayMs,
7484 uint32_t inDefaultTTL,
7485 int inPort,
7486 Boolean inLoopbackOnly,
7487 const char * inDomain,
7488 Boolean inBadUDPMode,
7489 DNSServerRef * outServer )
7490 {
7491 OSStatus err;
7492 DNSServerRef obj = NULL;
7493
7494 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
7495
7496 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7497
7498 ReplaceDispatchQueue( &obj->queue, inQueue );
7499 obj->eventHandler = inEventHandler;
7500 obj->eventContext = inEventContext;
7501 obj->responseDelayMs = inResponseDelayMs;
7502 obj->defaultTTL = inDefaultTTL;
7503 obj->port = inPort;
7504 obj->loopbackOnly = inLoopbackOnly;
7505 obj->badUDPMode = inBadUDPMode;
7506
7507 if( inDomain )
7508 {
7509 err = StringToDomainName( inDomain, &obj->domain, NULL );
7510 require_noerr_quiet( err, exit );
7511 }
7512 else
7513 {
7514 err = DomainNameDup( kDDotTestDomainName, &obj->domain, NULL );
7515 require_noerr_quiet( err, exit );
7516 }
7517
7518 *outServer = obj;
7519 obj = NULL;
7520 err = kNoErr;
7521
7522 exit:
7523 CFReleaseNullSafe( obj );
7524 return( err );
7525 }
7526
7527 //===========================================================================================================================
7528 // _DNSServerFinalize
7529 //===========================================================================================================================
7530
7531 static void _DNSServerFinalize( CFTypeRef inObj )
7532 {
7533 DNSServerRef const me = (DNSServerRef) inObj;
7534
7535 check( !me->readSourceUDPv4 );
7536 check( !me->readSourceUDPv6 );
7537 check( !me->readSourceTCPv4 );
7538 check( !me->readSourceTCPv6 );
7539 check( !me->responseTimer );
7540 ForgetMem( &me->domain );
7541 dispatch_forget( &me->queue );
7542 }
7543
7544 //===========================================================================================================================
7545 // DNSServerStart
7546 //===========================================================================================================================
7547
7548 static void _DNSServerStart( void *inContext );
7549 static void _DNSServerStop( void *inContext, OSStatus inError );
7550
7551 static void DNSServerStart( DNSServerRef me )
7552 {
7553 CFRetain( me );
7554 dispatch_async_f( me->queue, me, _DNSServerStart );
7555 }
7556
7557 static void _DNSServerStart( void *inContext )
7558 {
7559 OSStatus err;
7560 struct timeval now;
7561 DNSServerRef const me = (DNSServerRef) inContext;
7562 SocketRef sock = kInvalidSocketRef;
7563 SocketContext * sockCtx = NULL;
7564 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7565 int year, month, day;
7566
7567 // Create IPv4 UDP socket.
7568 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7569 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7570 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7571 // used for the remaining sockets.
7572
7573 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7574 me->port, &me->port, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7575 require_noerr( err, exit );
7576 check( me->port > 0 );
7577
7578 // Create read source for IPv4 UDP socket.
7579
7580 err = SocketContextCreate( sock, me, &sockCtx );
7581 require_noerr( err, exit );
7582 sock = kInvalidSocketRef;
7583
7584 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7585 &me->readSourceUDPv4 );
7586 require_noerr( err, exit );
7587 dispatch_resume( me->readSourceUDPv4 );
7588 me->sockUDPv4 = sockCtx->sock;
7589 sockCtx = NULL;
7590
7591 // Create IPv6 UDP socket.
7592
7593 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7594 me->port, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7595 require_noerr( err, exit );
7596
7597 // Create read source for IPv6 UDP socket.
7598
7599 err = SocketContextCreate( sock, me, &sockCtx );
7600 require_noerr( err, exit );
7601 sock = kInvalidSocketRef;
7602
7603 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7604 &me->readSourceUDPv6 );
7605 require_noerr( err, exit );
7606 dispatch_resume( me->readSourceUDPv6 );
7607 me->sockUDPv6 = sockCtx->sock;
7608 sockCtx = NULL;
7609
7610 // Create IPv4 TCP socket.
7611
7612 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7613 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7614 require_noerr( err, exit );
7615
7616 // Create read source for IPv4 TCP socket.
7617
7618 err = SocketContextCreate( sock, me, &sockCtx );
7619 require_noerr( err, exit );
7620 sock = kInvalidSocketRef;
7621
7622 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7623 &me->readSourceTCPv4 );
7624 require_noerr( err, exit );
7625 dispatch_resume( me->readSourceTCPv4 );
7626 sockCtx = NULL;
7627
7628 // Create IPv6 TCP socket.
7629
7630 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7631 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7632 require_noerr( err, exit );
7633
7634 // Create read source for IPv6 TCP socket.
7635
7636 err = SocketContextCreate( sock, me, &sockCtx );
7637 require_noerr( err, exit );
7638 sock = kInvalidSocketRef;
7639
7640 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7641 &me->readSourceTCPv6 );
7642 require_noerr( err, exit );
7643 dispatch_resume( me->readSourceTCPv6 );
7644 sockCtx = NULL;
7645
7646 ds_ulog( kLogLevelInfo, "Server is using port %d.\n", me->port );
7647 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, (uintptr_t) me->port, me->eventContext );
7648
7649 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7650 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7651
7652 gettimeofday( &now, NULL );
7653 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
7654 NULL, NULL, NULL );
7655 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
7656
7657 exit:
7658 ForgetSocket( &sock );
7659 if( sockCtx ) SocketContextRelease( sockCtx );
7660 if( err ) _DNSServerStop( me, err );
7661 }
7662
7663 //===========================================================================================================================
7664 // DNSServerStop
7665 //===========================================================================================================================
7666
7667 static void _DNSServerUserStop( void *inContext );
7668 static void _DNSServerStop2( void *inContext );
7669
7670 static void DNSServerStop( DNSServerRef me )
7671 {
7672 CFRetain( me );
7673 dispatch_async_f( me->queue, me, _DNSServerUserStop );
7674 }
7675
7676 static void _DNSServerUserStop( void *inContext )
7677 {
7678 DNSServerRef const me = (DNSServerRef) inContext;
7679
7680 _DNSServerStop( me, kNoErr );
7681 CFRelease( me );
7682 }
7683
7684 static void _DNSServerStop( void *inContext, OSStatus inError )
7685 {
7686 DNSServerRef const me = (DNSServerRef) inContext;
7687
7688 me->stopError = inError;
7689 dispatch_source_forget( &me->readSourceUDPv4 );
7690 dispatch_source_forget( &me->readSourceUDPv6 );
7691 dispatch_source_forget( &me->readSourceTCPv4 );
7692 dispatch_source_forget( &me->readSourceTCPv6 );
7693 dispatch_source_forget( &me->responseTimer );
7694 me->sockUDPv4 = kInvalidSocketRef;
7695 me->sockUDPv6 = kInvalidSocketRef;
7696
7697 if( me->responseList )
7698 {
7699 _DNSDelayedResponseFreeList( me->responseList );
7700 me->responseList = NULL;
7701 }
7702 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7703 }
7704
7705 static void _DNSServerStop2( void *inContext )
7706 {
7707 DNSServerRef const me = (DNSServerRef) inContext;
7708
7709 if( !me->stopped )
7710 {
7711 me->stopped = true;
7712 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, (uintptr_t) me->stopError, me->eventContext );
7713 CFRelease( me );
7714 }
7715 CFRelease( me );
7716 }
7717
7718 //===========================================================================================================================
7719 // _DNSDelayedResponseFree
7720 //===========================================================================================================================
7721
7722 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse )
7723 {
7724 ForgetMem( &inResponse->msgPtr );
7725 free( inResponse );
7726 }
7727
7728 //===========================================================================================================================
7729 // _DNSDelayedResponseFreeList
7730 //===========================================================================================================================
7731
7732 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList )
7733 {
7734 DNSDelayedResponse * response;
7735
7736 while( ( response = inList ) != NULL )
7737 {
7738 inList = response->next;
7739 _DNSDelayedResponseFree( response );
7740 }
7741 }
7742
7743 //===========================================================================================================================
7744 // _DNSServerUDPReadHandler
7745 //===========================================================================================================================
7746
7747 static OSStatus
7748 _DNSServerAnswerQuery(
7749 DNSServerRef inServer,
7750 const uint8_t * inQueryPtr,
7751 size_t inQueryLen,
7752 Boolean inForTCP,
7753 uint8_t ** outResponsePtr,
7754 size_t * outResponseLen );
7755
7756 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7757 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7758
7759 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7760 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7761
7762 static OSStatus
7763 _DNSServerScheduleDelayedResponse(
7764 DNSServerRef inServer,
7765 const struct sockaddr * inDestAddr,
7766 uint8_t * inMsgPtr,
7767 size_t inMsgLen );
7768 static void _DNSServerUDPDelayedSend( void *inContext );
7769
7770 static void _DNSServerUDPReadHandler( void *inContext )
7771 {
7772 OSStatus err;
7773 SocketContext * const sockCtx = (SocketContext *) inContext;
7774 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7775 struct timeval now;
7776 ssize_t n;
7777 sockaddr_ip clientAddr;
7778 socklen_t clientAddrLen;
7779 uint8_t * responsePtr = NULL; // malloc'd
7780 size_t responseLen;
7781 uint8_t msg[ 512 ];
7782
7783 gettimeofday( &now, NULL );
7784
7785 // Receive message.
7786
7787 clientAddrLen = (socklen_t) sizeof( clientAddr );
7788 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7789 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7790 require_noerr( err, exit );
7791
7792 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7793
7794 if( n < kDNSHeaderLength )
7795 {
7796 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7797 goto exit;
7798 }
7799
7800 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7801
7802 // Create response.
7803
7804 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, &responsePtr, &responseLen );
7805 require_noerr_quiet( err, exit );
7806
7807 // Schedule response.
7808
7809 if( me->responseDelayMs > 0 )
7810 {
7811 err = _DNSServerScheduleDelayedResponse( me, &clientAddr.sa, responsePtr, responseLen );
7812 require_noerr( err, exit );
7813 responsePtr = NULL;
7814 }
7815 else
7816 {
7817 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7818
7819 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7820 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7821 require_noerr( err, exit );
7822 }
7823
7824 exit:
7825 FreeNullSafe( responsePtr );
7826 return;
7827 }
7828
7829 static OSStatus
7830 _DNSServerScheduleDelayedResponse(
7831 DNSServerRef me,
7832 const struct sockaddr * inDestAddr,
7833 uint8_t * inMsgPtr,
7834 size_t inMsgLen )
7835 {
7836 OSStatus err;
7837 DNSDelayedResponse * response;
7838 DNSDelayedResponse ** responsePtr;
7839 DNSDelayedResponse * newResponse;
7840 uint64_t targetTicks;
7841
7842 targetTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
7843
7844 newResponse = (DNSDelayedResponse *) calloc( 1, sizeof( *newResponse ) );
7845 require_action( newResponse, exit, err = kNoMemoryErr );
7846
7847 if( !me->responseList || ( targetTicks < me->responseList->targetTicks ) )
7848 {
7849 dispatch_source_forget( &me->responseTimer );
7850
7851 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7852 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, _DNSServerUDPDelayedSend,
7853 NULL, me, &me->responseTimer );
7854 require_noerr( err, exit );
7855 dispatch_resume( me->responseTimer );
7856 }
7857
7858 SockAddrCopy( inDestAddr, &newResponse->destAddr );
7859 newResponse->targetTicks = targetTicks;
7860 newResponse->msgPtr = inMsgPtr;
7861 newResponse->msgLen = inMsgLen;
7862
7863 for( responsePtr = &me->responseList; ( response = *responsePtr ) != NULL; responsePtr = &response->next )
7864 {
7865 if( newResponse->targetTicks < response->targetTicks ) break;
7866 }
7867 newResponse->next = response;
7868 *responsePtr = newResponse;
7869 newResponse = NULL;
7870 err = kNoErr;
7871
7872 exit:
7873 if( newResponse ) _DNSDelayedResponseFree( newResponse );
7874 return( err );
7875 }
7876
7877 static void _DNSServerUDPDelayedSend( void *inContext )
7878 {
7879 OSStatus err;
7880 DNSServerRef const me = (DNSServerRef) inContext;
7881 DNSDelayedResponse * response;
7882 SocketRef sock;
7883 ssize_t n;
7884 uint64_t nowTicks;
7885 uint64_t remainingNs;
7886 DNSDelayedResponse * freeList = NULL;
7887
7888 dispatch_source_forget( &me->responseTimer );
7889
7890 nowTicks = UpTicks();
7891 while( ( ( response = me->responseList ) != NULL ) && ( response->targetTicks <= nowTicks ) )
7892 {
7893 me->responseList = response->next;
7894
7895 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7896 response->msgLen, response->msgPtr, response->msgLen );
7897
7898 sock = ( response->destAddr.sa.sa_family == AF_INET ) ? me->sockUDPv4 : me->sockUDPv6;
7899 n = sendto( sock, (char *) response->msgPtr, response->msgLen, 0, &response->destAddr.sa,
7900 SockAddrGetSize( &response->destAddr ) );
7901 err = map_socket_value_errno( sock, n == (ssize_t) response->msgLen, n );
7902 check_noerr( err );
7903
7904 response->next = freeList;
7905 freeList = response;
7906 nowTicks = UpTicks();
7907 }
7908
7909 if( response )
7910 {
7911 check( response->targetTicks > nowTicks );
7912 remainingNs = UpTicksToNanoseconds( response->targetTicks - nowTicks );
7913 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7914
7915 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7916 me->queue, _DNSServerUDPDelayedSend, NULL, me, &me->responseTimer );
7917 require_noerr( err, exit );
7918 dispatch_resume( me->responseTimer );
7919 }
7920
7921 exit:
7922 if( freeList ) _DNSDelayedResponseFreeList( freeList );
7923 }
7924
7925 //===========================================================================================================================
7926 // _DNSServerAnswerQuery
7927 //===========================================================================================================================
7928
7929 #define kLabelPrefix_Alias "alias"
7930 #define kLabelPrefix_AliasTTL "alias-ttl"
7931 #define kLabelPrefix_Count "count-"
7932 #define kLabelPrefix_Tag "tag-"
7933 #define kLabelPrefix_TTL "ttl-"
7934 #define kLabel_IPv4 "ipv4"
7935 #define kLabel_IPv6 "ipv6"
7936 #define kLabelPrefix_SRV "srv-"
7937
7938 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7939 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7940
7941 typedef struct
7942 {
7943 uint16_t priority; // Priority from SRV label.
7944 uint16_t weight; // Weight from SRV label.
7945 uint16_t port; // Port number from SRV label.
7946 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
7947 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
7948
7949 } ParsedSRV;
7950
7951 static OSStatus
7952 _DNSServerInitializeResponseMessage(
7953 DataBuffer * inDB,
7954 unsigned int inID,
7955 unsigned int inFlags,
7956 const uint8_t * inQName,
7957 unsigned int inQType,
7958 unsigned int inQClass );
7959 static OSStatus
7960 _DNSServerAnswerQueryDynamically(
7961 DNSServerRef inServer,
7962 const uint8_t * inQName,
7963 unsigned int inQType,
7964 unsigned int inQClass,
7965 Boolean inForTCP,
7966 DataBuffer * inDB );
7967 static Boolean
7968 _DNSServerNameIsSRVName(
7969 DNSServerRef inServer,
7970 const uint8_t * inName,
7971 const uint8_t ** outDomainPtr,
7972 size_t * outDomainLen,
7973 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
7974 size_t * outSRVCount );
7975 static Boolean
7976 _DNSServerNameIsHostname(
7977 DNSServerRef inServer,
7978 const uint8_t * inName,
7979 uint32_t * outAliasCount,
7980 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
7981 size_t * outAliasTTLCount,
7982 unsigned int * outCount,
7983 unsigned int * outRandCount,
7984 uint32_t * outTTL,
7985 Boolean * outHasA,
7986 Boolean * outHasAAAA,
7987 Boolean * outHasSOA );
7988
7989 static OSStatus
7990 _DNSServerAnswerQuery(
7991 DNSServerRef me,
7992 const uint8_t * const inQueryPtr,
7993 const size_t inQueryLen,
7994 Boolean inForTCP,
7995 uint8_t ** outResponsePtr,
7996 size_t * outResponseLen )
7997 {
7998 OSStatus err;
7999 DataBuffer dataBuf;
8000 const uint8_t * ptr;
8001 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
8002 const DNSHeader * qhdr;
8003 const dns_fixed_fields_question * fields;
8004 unsigned int msgID, qflags, qtype, qclass, rflags;
8005 uint8_t qname[ kDomainNameLengthMax ];
8006
8007 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
8008
8009 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
8010
8011 qhdr = (const DNSHeader *) inQueryPtr;
8012 msgID = DNSHeaderGetID( qhdr );
8013 qflags = DNSHeaderGetFlags( qhdr );
8014
8015 // Minimal checking of the query message's header.
8016
8017 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
8018 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
8019 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
8020 {
8021 err = kRequestErr;
8022 goto exit;
8023 }
8024
8025 // Get QNAME.
8026
8027 ptr = (const uint8_t *) &qhdr[ 1 ];
8028 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
8029 require_noerr( err, exit );
8030
8031 // Get QTYPE and QCLASS.
8032
8033 require_action_quiet( ( (size_t)( queryEnd - ptr ) ) >= sizeof( *fields ), exit, err = kUnderrunErr );
8034 fields = (const dns_fixed_fields_question *) ptr;
8035 qtype = dns_fixed_fields_question_get_type( fields );
8036 qclass = dns_fixed_fields_question_get_class( fields );
8037
8038 // Create a tentative response message.
8039
8040 rflags = kDNSHeaderFlag_Response;
8041 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
8042 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
8043
8044 if( me->badUDPMode && !inForTCP ) msgID = (uint16_t)( msgID + 1 );
8045 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
8046 require_noerr( err, exit );
8047
8048 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inForTCP, &dataBuf );
8049 if( err )
8050 {
8051 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
8052 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
8053 require_noerr( err, exit );
8054 }
8055
8056 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
8057 require_noerr( err, exit );
8058
8059 exit:
8060 DataBuffer_Free( &dataBuf );
8061 return( err );
8062 }
8063
8064 static OSStatus
8065 _DNSServerInitializeResponseMessage(
8066 DataBuffer * inDB,
8067 unsigned int inID,
8068 unsigned int inFlags,
8069 const uint8_t * inQName,
8070 unsigned int inQType,
8071 unsigned int inQClass )
8072 {
8073 OSStatus err;
8074 DNSHeader header;
8075
8076 DataBuffer_Reset( inDB );
8077
8078 memset( &header, 0, sizeof( header ) );
8079 DNSHeaderSetID( &header, inID );
8080 DNSHeaderSetFlags( &header, inFlags );
8081 DNSHeaderSetQuestionCount( &header, 1 );
8082
8083 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
8084 require_noerr( err, exit );
8085
8086 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), (uint16_t) inQType,
8087 (uint16_t) inQClass );
8088 require_noerr( err, exit );
8089
8090 exit:
8091 return( err );
8092 }
8093
8094 static OSStatus
8095 _DNSServerAnswerQueryDynamically(
8096 DNSServerRef me,
8097 const uint8_t * const inQName,
8098 const unsigned int inQType,
8099 const unsigned int inQClass,
8100 const Boolean inForTCP,
8101 DataBuffer * const inDB )
8102 {
8103 OSStatus err;
8104 DNSHeader * hdr;
8105 unsigned int flags, rcode;
8106 uint32_t aliasCount, i;
8107 uint32_t aliasTTLs[ kMaxAliasTTLCount ];
8108 size_t aliasTTLCount;
8109 unsigned int addrCount, randCount;
8110 uint32_t ttl;
8111 ParsedSRV srvArray[ kMaxParsedSRVCount ];
8112 size_t srvCount;
8113 const uint8_t * srvDomainPtr;
8114 size_t srvDomainLen;
8115 unsigned int answerCount;
8116 Boolean notImplemented, truncated;
8117 Boolean useAliasTTLs, nameExists, nameHasA, nameHasAAAA, nameHasSRV, nameHasSOA;
8118 uint8_t namePtr[ 2 ];
8119 dns_fixed_fields_record fields;
8120
8121 answerCount = 0;
8122 truncated = false;
8123 nameExists = false;
8124 require_action_quiet( inQClass == kDNSServiceClass_IN, done, notImplemented = true );
8125
8126 notImplemented = false;
8127 aliasCount = 0;
8128 nameHasA = false;
8129 nameHasAAAA = false;
8130 nameHasSOA = false;
8131 useAliasTTLs = false;
8132 nameHasSRV = false;
8133 srvDomainLen = 0;
8134 srvCount = 0;
8135
8136 if( _DNSServerNameIsHostname( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount, &ttl,
8137 &nameHasA, &nameHasAAAA, &nameHasSOA ) )
8138 {
8139 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
8140 check( ( addrCount >= 1 ) && ( addrCount <= 255 ) );
8141 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
8142 check( nameHasA || nameHasAAAA );
8143
8144 if( aliasTTLCount > 0 )
8145 {
8146 aliasCount = (uint32_t) aliasTTLCount;
8147 useAliasTTLs = true;
8148 }
8149 nameExists = true;
8150 }
8151 else if( _DNSServerNameIsSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) )
8152 {
8153 nameHasSRV = true;
8154 nameExists = true;
8155 }
8156 require_quiet( nameExists, done );
8157
8158 if( aliasCount > 0 )
8159 {
8160 size_t nameOffset;
8161 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
8162
8163 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
8164 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
8165 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
8166 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
8167
8168 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
8169
8170 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
8171
8172 nameOffset = kDNSHeaderLength;
8173
8174 for( i = aliasCount; i >= 1; --i )
8175 {
8176 size_t nameLen;
8177 size_t rdataLen;
8178 uint32_t j;
8179 uint32_t aliasTTL;
8180 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
8181
8182 if( nameOffset <= kDNSCompressionOffsetMax )
8183 {
8184 DNSMessageWriteLabelPointer( namePtr, nameOffset );
8185 nameLen = sizeof( namePtr );
8186 }
8187 else
8188 {
8189 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
8190 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
8191 }
8192
8193 if( i >= 2 )
8194 {
8195 char * dst = (char *) &rdataLabel[ 1 ];
8196 char * const lim = (char *) &rdataLabel[ countof( rdataLabel ) ];
8197
8198 if( useAliasTTLs )
8199 {
8200 err = SNPrintF_Add( &dst, lim, kLabelPrefix_AliasTTL );
8201 require_noerr( err, exit );
8202
8203 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
8204 {
8205 err = SNPrintF_Add( &dst, lim, "-%u", aliasTTLs[ j ] );
8206 require_noerr( err, exit );
8207 }
8208 }
8209 else
8210 {
8211 err = SNPrintF_Add( &dst, lim, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
8212 require_noerr( err, exit );
8213 }
8214 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
8215 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
8216 }
8217 else
8218 {
8219 rdataLen = sizeof( superPtr );
8220 }
8221
8222 if( !inForTCP )
8223 {
8224 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
8225
8226 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8227 {
8228 truncated = true;
8229 goto done;
8230 }
8231 }
8232 ++answerCount;
8233
8234 // Set CNAME record's NAME.
8235
8236 if( nameOffset <= kDNSCompressionOffsetMax )
8237 {
8238 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8239 require_noerr( err, exit );
8240 }
8241 else
8242 {
8243 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
8244 require_noerr( err, exit );
8245
8246 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8247 require_noerr( err, exit );
8248 }
8249
8250 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8251
8252 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
8253 dns_fixed_fields_record_init( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
8254 (uint16_t) rdataLen );
8255 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8256 require_noerr( err, exit );
8257
8258 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8259
8260 nameOffset = DataBuffer_GetLen( inDB );
8261
8262 // Set CNAME record's RDATA.
8263
8264 if( i >= 2 )
8265 {
8266 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
8267 require_noerr( err, exit );
8268 }
8269 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8270 require_noerr( err, exit );
8271 }
8272
8273 namePtr[ 0 ] = superPtr[ 0 ];
8274 namePtr[ 1 ] = superPtr[ 1 ];
8275 }
8276 else
8277 {
8278 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8279
8280 DNSMessageWriteLabelPointer( namePtr, kDNSHeaderLength );
8281 }
8282
8283 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
8284 {
8285 uint8_t * lsb; // Pointer to the least significant byte of record data.
8286 size_t recordLen; // Length of the entire record.
8287 size_t rdataLen; // Length of record's RDATA.
8288 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8289 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
8290 const int useBadAddrs = ( me->badUDPMode && !inForTCP ) ? true : false;
8291
8292 if( inQType == kDNSServiceType_A )
8293 {
8294 const uint32_t baseAddrV4 = useBadAddrs ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
8295
8296 require_quiet( nameHasA, done );
8297
8298 rdataLen = 4;
8299 WriteBig32( rdata, baseAddrV4 );
8300 lsb = &rdata[ 3 ];
8301 }
8302 else
8303 {
8304 const uint8_t * const baseAddrV6 = useBadAddrs ? kDNSServerBadBaseAddrV6 : kDNSServerBaseAddrV6;
8305
8306 require_quiet( nameHasAAAA, done );
8307
8308 rdataLen = 16;
8309 memcpy( rdata, baseAddrV6, 16 );
8310 lsb = &rdata[ 15 ];
8311 }
8312
8313 if( randCount > 0 )
8314 {
8315 // Populate the array with all integers between 1 and <randCount>, inclusive.
8316
8317 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
8318
8319 // Prevent dubious static analyzer warning.
8320 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8321 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8322 // for some reason.
8323
8324 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
8325 addrCount, randCount );
8326
8327 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8328 // 1 and <randCount>, inclusive.
8329 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8330 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8331
8332 for( i = 0; i < addrCount; ++i )
8333 {
8334 uint8_t tmp;
8335 uint32_t j;
8336
8337 j = RandomRange( i, randCount - 1 );
8338 if( i != j )
8339 {
8340 tmp = randIntegers[ i ];
8341 randIntegers[ i ] = randIntegers[ j ];
8342 randIntegers[ j ] = tmp;
8343 }
8344 }
8345 }
8346
8347 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8348 for( i = 0; i < addrCount; ++i )
8349 {
8350 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8351 {
8352 truncated = true;
8353 goto done;
8354 }
8355
8356 // Set record NAME.
8357
8358 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8359 require_noerr( err, exit );
8360
8361 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8362
8363 dns_fixed_fields_record_init( &fields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
8364 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8365 require_noerr( err, exit );
8366
8367 // Set record RDATA.
8368
8369 *lsb = ( randCount > 0 ) ? randIntegers[ i ] : ( *lsb + 1 );
8370
8371 err = DataBuffer_Append( inDB, rdata, rdataLen );
8372 require_noerr( err, exit );
8373
8374 ++answerCount;
8375 }
8376 }
8377 else if( inQType == kDNSServiceType_SRV )
8378 {
8379 require_quiet( nameHasSRV, done );
8380
8381 dns_fixed_fields_record_init( &fields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
8382
8383 for( i = 0; i < srvCount; ++i )
8384 {
8385 dns_fixed_fields_srv fieldsSRV;
8386 size_t rdataLen;
8387 size_t recordLen;
8388 const ParsedSRV * const srv = &srvArray[ i ];
8389
8390 rdataLen = sizeof( fieldsSRV ) + srvDomainLen + srv->targetLen + 1;
8391 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8392
8393 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8394 {
8395 truncated = true;
8396 goto done;
8397 }
8398
8399 // Append record NAME.
8400
8401 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8402 require_noerr( err, exit );
8403
8404 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8405
8406 WriteBig16( fields.rdlength, rdataLen );
8407 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8408 require_noerr( err, exit );
8409
8410 // Append SRV RDATA.
8411
8412 dns_fixed_fields_srv_init( &fieldsSRV, srv->priority, srv->weight, srv->port );
8413
8414 err = DataBuffer_Append( inDB, &fieldsSRV, sizeof( fieldsSRV ) );
8415 require_noerr( err, exit );
8416
8417 if( srv->targetLen > 0 )
8418 {
8419 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
8420 require_noerr( err, exit );
8421 }
8422
8423 if( srvDomainLen > 0 )
8424 {
8425 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
8426 require_noerr( err, exit );
8427 }
8428
8429 err = DataBuffer_Append( inDB, "", 1 ); // Append root label.
8430 require_noerr( err, exit );
8431
8432 ++answerCount;
8433 }
8434 }
8435 else if( inQType == kDNSServiceType_SOA )
8436 {
8437 size_t nameLen, recordLen;
8438
8439 require_quiet( nameHasSOA, done );
8440
8441 nameLen = DomainNameLength( me->domain );
8442 if( !inForTCP )
8443 {
8444 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
8445 require_noerr( err, exit );
8446
8447 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8448 {
8449 truncated = true;
8450 goto done;
8451 }
8452 }
8453
8454 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
8455 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
8456 me->defaultTTL, NULL );
8457 require_noerr( err, exit );
8458
8459 ++answerCount;
8460 }
8461
8462 done:
8463 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8464 flags = DNSHeaderGetFlags( hdr );
8465 if( notImplemented )
8466 {
8467 rcode = kDNSRCode_NotImplemented;
8468 }
8469 else
8470 {
8471 flags |= kDNSHeaderFlag_AuthAnswer;
8472 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8473 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8474 }
8475 DNSFlagsSetRCode( flags, rcode );
8476 DNSHeaderSetFlags( hdr, flags );
8477 DNSHeaderSetAnswerCount( hdr, answerCount );
8478 err = kNoErr;
8479
8480 exit:
8481 return( err );
8482 }
8483
8484 static Boolean
8485 _DNSServerNameIsHostname(
8486 DNSServerRef me,
8487 const uint8_t * inName,
8488 uint32_t * outAliasCount,
8489 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
8490 size_t * outAliasTTLCount,
8491 unsigned int * outCount,
8492 unsigned int * outRandCount,
8493 uint32_t * outTTL,
8494 Boolean * outHasA,
8495 Boolean * outHasAAAA,
8496 Boolean * outHasSOA )
8497 {
8498 OSStatus err;
8499 const uint8_t * label;
8500 const uint8_t * nextLabel;
8501 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8502 unsigned int count = 0; // First arg from Count label. Valid values are in [1, 255].
8503 unsigned int randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
8504 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8505 size_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
8506 int hasTagLabel = false;
8507 int hasIPv4Label = false;
8508 int hasIPv6Label = false;
8509 int isNameValid = false;
8510
8511 for( label = inName; label[ 0 ]; label = nextLabel )
8512 {
8513 uint32_t arg;
8514
8515 nextLabel = &label[ 1 + label[ 0 ] ];
8516
8517 // Check if the first label is a valid alias TTL sequence label.
8518
8519 if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_AliasTTL ) == 0 ) )
8520 {
8521 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_AliasTTL ) ];
8522 const char * const end = (const char *) nextLabel;
8523 const char * next;
8524
8525 check( label[ 0 ] <= kDomainLabelLengthMax );
8526
8527 while( ptr < end )
8528 {
8529 if( *ptr != '-' ) break;
8530 ++ptr;
8531 err = DecimalTextToUInt32( ptr, end, &arg, &next );
8532 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8533 inAliasTTLs[ aliasTTLCount++ ] = arg;
8534 ptr = next;
8535 }
8536 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
8537 }
8538
8539 // Check if the first label is a valid alias label.
8540
8541 else if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Alias ) == 0 ) )
8542 {
8543 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Alias ) ];
8544 const char * const end = (const char *) nextLabel;
8545
8546 if( ptr < end )
8547 {
8548 if( *ptr++ != '-' ) break;
8549 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8550 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
8551 aliasCount = arg;
8552 if( ptr != end ) break;
8553 }
8554 else
8555 {
8556 aliasCount = 1;
8557 }
8558 }
8559
8560 // Check if this label is a valid count label.
8561
8562 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Count ) == 0 )
8563 {
8564 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Count ) ];
8565 const char * const end = (const char *) nextLabel;
8566
8567 if( count > 0 ) break; // Count cannot be specified more than once.
8568
8569 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8570 if( err || ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be in [1, 255].
8571 count = (unsigned int) arg;
8572
8573 if( ptr < end )
8574 {
8575 if( *ptr++ != '-' ) break;
8576 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8577 if( err || ( arg < (uint32_t) count ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
8578 randCount = (unsigned int) arg;
8579 if( ptr != end ) break;
8580 }
8581 }
8582
8583 // Check if this label is a valid TTL label.
8584
8585 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_TTL ) == 0 )
8586 {
8587 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_TTL ) ];
8588 const char * const end = (const char *) nextLabel;
8589
8590 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
8591
8592 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8593 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8594 ttl = (int32_t) arg;
8595 if( ptr != end ) break;
8596 }
8597
8598 // Check if this label is a valid IPv4 label.
8599
8600 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv4 ) == 0 )
8601 {
8602 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8603 hasIPv4Label = true;
8604 }
8605
8606 // Check if this label is a valid IPv6 label.
8607
8608 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv6 ) == 0 )
8609 {
8610 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8611 hasIPv6Label = true;
8612 }
8613
8614 // Check if this label is a valid tag label.
8615
8616 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Tag ) == 0 )
8617 {
8618 hasTagLabel = true;
8619 }
8620
8621 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8622 // In both cases, there are no more labels to check.
8623
8624 else
8625 {
8626 if( DomainNameEqual( label, me->domain ) ) isNameValid = true;
8627 break;
8628 }
8629 }
8630 require_quiet( isNameValid, exit );
8631
8632 if( outAliasCount ) *outAliasCount = aliasCount;
8633 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
8634 if( outCount ) *outCount = ( count > 0 ) ? count : 1;
8635 if( outRandCount ) *outRandCount = randCount;
8636 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
8637 if( outHasA ) *outHasA = ( hasIPv4Label || !hasIPv6Label ) ? true : false;
8638 if( outHasAAAA ) *outHasAAAA = ( hasIPv6Label || !hasIPv4Label ) ? true : false;
8639 if( outHasSOA )
8640 {
8641 *outHasSOA = ( !count && ( ttl < 0 ) && !hasIPv4Label && !hasIPv6Label && !hasTagLabel ) ? true : false;
8642 }
8643
8644 exit:
8645 return( isNameValid ? true : false );
8646 }
8647
8648 static Boolean
8649 _DNSServerNameIsSRVName(
8650 DNSServerRef me,
8651 const uint8_t * inName,
8652 const uint8_t ** outDomainPtr,
8653 size_t * outDomainLen,
8654 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
8655 size_t * outSRVCount )
8656 {
8657 OSStatus err;
8658 const uint8_t * label;
8659 const uint8_t * domainPtr;
8660 size_t domainLen;
8661 size_t srvCount;
8662 uint32_t arg;
8663 int isNameValid = false;
8664
8665 label = inName;
8666
8667 // Ensure that first label, i.e, the service label, begins with a '_' character.
8668
8669 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8670 label = DomainNameGetNextLabel( label );
8671
8672 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8673
8674 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8675 label = DomainNameGetNextLabel( label );
8676
8677 // Parse the domain name, if any.
8678
8679 domainPtr = label;
8680 while( *label )
8681 {
8682 if( DomainNameEqual( label, me->domain ) ||
8683 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8684 label = DomainNameGetNextLabel( label );
8685 }
8686 require_quiet( *label, exit );
8687
8688 domainLen = (size_t)( label - domainPtr );
8689
8690 // Parse SRV labels, if any.
8691
8692 srvCount = 0;
8693 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
8694 {
8695 const uint8_t * const nextLabel = DomainNameGetNextLabel( label );
8696 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
8697 const char * const end = (const char *) nextLabel;
8698 const uint8_t * target;
8699 unsigned int priority, weight, port;
8700
8701 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8702 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8703 priority = (unsigned int) arg;
8704
8705 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8706 ++ptr;
8707
8708 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8709 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8710 weight = (unsigned int) arg;
8711
8712 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8713 ++ptr;
8714
8715 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8716 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8717 port = (unsigned int) arg;
8718
8719 require_quiet( ptr == end, exit );
8720
8721 target = nextLabel;
8722 for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
8723 {
8724 if( DomainNameEqual( label, me->domain ) ||
8725 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8726 }
8727 require_quiet( *label, exit );
8728
8729 if( inSRVArray )
8730 {
8731 inSRVArray[ srvCount ].priority = (uint16_t) priority;
8732 inSRVArray[ srvCount ].weight = (uint16_t) weight;
8733 inSRVArray[ srvCount ].port = (uint16_t) port;
8734 inSRVArray[ srvCount ].targetPtr = target;
8735 inSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
8736 }
8737 ++srvCount;
8738 }
8739 require_quiet( DomainNameEqual( label, me->domain ), exit );
8740 isNameValid = true;
8741
8742 if( outDomainPtr ) *outDomainPtr = domainPtr;
8743 if( outDomainLen ) *outDomainLen = domainLen;
8744 if( outSRVCount ) *outSRVCount = srvCount;
8745
8746 exit:
8747 return( isNameValid ? true : false );
8748 }
8749
8750 //===========================================================================================================================
8751 // _DNSServerTCPReadHandler
8752 //===========================================================================================================================
8753
8754 typedef struct
8755 {
8756 DNSServerRef server; // Reference to DNS server object.
8757 sockaddr_ip clientAddr; // Client's address.
8758 dispatch_source_t readSource; // Dispatch read source for client socket.
8759 dispatch_source_t writeSource; // Dispatch write source for client socket.
8760 size_t offset; // Offset into receive buffer.
8761 void * msgPtr; // Pointer to dynamically allocated message buffer.
8762 size_t msgLen; // Length of message buffer.
8763 Boolean readSuspended; // True if the read source is currently suspended.
8764 Boolean writeSuspended; // True if the write source is currently suspended.
8765 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8766 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8767 iovec_t iov[ 2 ]; // IO vector for writing response message.
8768 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8769 int iovCount; // Vector count for SocketWriteData().
8770
8771 } TCPConnectionContext;
8772
8773 static void TCPConnectionStop( TCPConnectionContext *inContext );
8774 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8775 static void TCPConnectionReadHandler( void *inContext );
8776 static void TCPConnectionWriteHandler( void *inContext );
8777
8778 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8779
8780 static void _DNSServerTCPReadHandler( void *inContext )
8781 {
8782 OSStatus err;
8783 SocketContext * const sockCtx = (SocketContext *) inContext;
8784 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
8785 TCPConnectionContext * connection;
8786 socklen_t clientAddrLen;
8787 SocketRef newSock = kInvalidSocketRef;
8788 SocketContext * newSockCtx = NULL;
8789
8790 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8791 require_action( connection, exit, err = kNoMemoryErr );
8792
8793 CFRetain( me );
8794 connection->server = me;
8795
8796 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8797 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8798 err = map_socket_creation_errno( newSock );
8799 require_noerr( err, exit );
8800
8801 err = SocketContextCreate( newSock, connection, &newSockCtx );
8802 require_noerr( err, exit );
8803 newSock = kInvalidSocketRef;
8804
8805 err = DispatchReadSourceCreate( newSockCtx->sock, me->queue, TCPConnectionReadHandler, SocketContextCancelHandler,
8806 newSockCtx, &connection->readSource );
8807 require_noerr( err, exit );
8808 SocketContextRetain( newSockCtx );
8809 dispatch_resume( connection->readSource );
8810
8811 err = DispatchWriteSourceCreate( newSockCtx->sock, me->queue, TCPConnectionWriteHandler, SocketContextCancelHandler,
8812 newSockCtx, &connection->writeSource );
8813 require_noerr( err, exit );
8814 SocketContextRetain( newSockCtx );
8815 connection->writeSuspended = true;
8816 connection = NULL;
8817
8818 exit:
8819 ForgetSocket( &newSock );
8820 SocketContextRelease( newSockCtx );
8821 TCPConnectionForget( &connection );
8822 }
8823
8824 //===========================================================================================================================
8825 // TCPConnectionStop
8826 //===========================================================================================================================
8827
8828 static void TCPConnectionStop( TCPConnectionContext *inContext )
8829 {
8830 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8831 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8832 }
8833
8834 //===========================================================================================================================
8835 // TCPConnectionContextFree
8836 //===========================================================================================================================
8837
8838 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8839 {
8840 check( !inContext->readSource );
8841 check( !inContext->writeSource );
8842 ForgetCF( &inContext->server );
8843 ForgetMem( &inContext->msgPtr );
8844 free( inContext );
8845 }
8846
8847 //===========================================================================================================================
8848 // TCPConnectionReadHandler
8849 //===========================================================================================================================
8850
8851 static void TCPConnectionReadHandler( void *inContext )
8852 {
8853 OSStatus err;
8854 SocketContext * const sockCtx = (SocketContext *) inContext;
8855 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8856 struct timeval now;
8857 uint8_t * responsePtr = NULL; // malloc'd
8858 size_t responseLen;
8859
8860 // Receive message length.
8861
8862 if( !connection->receivedLength )
8863 {
8864 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8865 if( err == EWOULDBLOCK ) goto exit;
8866 require_noerr( err, exit );
8867
8868 connection->offset = 0;
8869 connection->msgLen = ReadBig16( connection->lenBuf );
8870 connection->msgPtr = malloc( connection->msgLen );
8871 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8872 connection->receivedLength = true;
8873 }
8874
8875 // Receive message.
8876
8877 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8878 if( err == EWOULDBLOCK ) goto exit;
8879 require_noerr( err, exit );
8880
8881 gettimeofday( &now, NULL );
8882 dispatch_suspend( connection->readSource );
8883 connection->readSuspended = true;
8884
8885 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8886 connection->msgLen, &connection->clientAddr, &now );
8887
8888 if( connection->msgLen < kDNSHeaderLength )
8889 {
8890 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8891 goto exit;
8892 }
8893
8894 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8895
8896 // Create response.
8897
8898 err = _DNSServerAnswerQueryForTCP( connection->server, connection->msgPtr, connection->msgLen, &responsePtr,
8899 &responseLen );
8900 require_noerr_quiet( err, exit );
8901
8902 // Send response.
8903
8904 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8905
8906 free( connection->msgPtr );
8907 connection->msgPtr = responsePtr;
8908 connection->msgLen = responseLen;
8909 responsePtr = NULL;
8910
8911 check( connection->msgLen <= UINT16_MAX );
8912 WriteBig16( connection->lenBuf, connection->msgLen );
8913 connection->iov[ 0 ].iov_base = connection->lenBuf;
8914 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8915 connection->iov[ 1 ].iov_base = connection->msgPtr;
8916 connection->iov[ 1 ].iov_len = connection->msgLen;
8917
8918 connection->iovPtr = connection->iov;
8919 connection->iovCount = 2;
8920
8921 check( connection->writeSuspended );
8922 dispatch_resume( connection->writeSource );
8923 connection->writeSuspended = false;
8924
8925 exit:
8926 FreeNullSafe( responsePtr );
8927 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8928 }
8929
8930 //===========================================================================================================================
8931 // TCPConnectionWriteHandler
8932 //===========================================================================================================================
8933
8934 static void TCPConnectionWriteHandler( void *inContext )
8935 {
8936 OSStatus err;
8937 SocketContext * const sockCtx = (SocketContext *) inContext;
8938 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8939
8940 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8941 if( err == EWOULDBLOCK ) goto exit;
8942 check_noerr( err );
8943
8944 TCPConnectionForget( &connection );
8945
8946 exit:
8947 return;
8948 }
8949
8950 //===========================================================================================================================
8951 // MDNSReplierCmd
8952 //===========================================================================================================================
8953
8954 typedef struct
8955 {
8956 uint8_t * hostname; // Used as the base name for hostnames and service names.
8957 uint8_t * serviceLabel; // Label containing the base service name.
8958 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
8959 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
8960 size_t bitmapCount; // Number of 64-bit bitmaps.
8961 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
8962 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
8963 uint32_t ifIndex; // Index of the interface to run on.
8964 unsigned int recordCountA; // Number of A records per hostname.
8965 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
8966 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8967 double ucastDropRate; // Probability of dropping a unicast response.
8968 double mcastDropRate; // Probability of dropping a multicast query or response.
8969 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8970 Boolean noAdditionals; // True if responses are to not include additional records.
8971 Boolean useIPv4; // True if the replier is to use IPv4.
8972 Boolean useIPv6; // True if the replier is to use IPv6.
8973 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
8974 #if( TARGET_OS_DARWIN )
8975 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
8976 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
8977 #endif
8978
8979 } MDNSReplierContext;
8980
8981 typedef struct MRResourceRecord MRResourceRecord;
8982 struct MRResourceRecord
8983 {
8984 MRResourceRecord * next; // Next item in list.
8985 uint8_t * name; // Resource record name.
8986 uint16_t type; // Resource record type.
8987 uint16_t class; // Resource record class.
8988 uint32_t ttl; // Resource record TTL.
8989 uint16_t rdlength; // Resource record data length.
8990 uint8_t * rdata; // Resource record data.
8991 const uint8_t * target; // For SRV records, pointer to target in RDATA.
8992 };
8993
8994 typedef struct MRNameOffsetItem MRNameOffsetItem;
8995 struct MRNameOffsetItem
8996 {
8997 MRNameOffsetItem * next; // Next item in list.
8998 uint16_t offset; // Offset of domain name in response message.
8999 uint8_t name[ 1 ]; // Variable-length array for domain name.
9000 };
9001
9002 #if( TARGET_OS_DARWIN )
9003 static void _MDNSReplierFollowedProcessHandler( void *inContext );
9004 #endif
9005 static void _MDNSReplierReadHandler( void *inContext );
9006 static OSStatus
9007 _MDNSReplierAnswerQuery(
9008 MDNSReplierContext * inContext,
9009 const uint8_t * inQueryPtr,
9010 size_t inQueryLen,
9011 sockaddr_ip * inSender,
9012 SocketRef inSock,
9013 unsigned int inIndex );
9014 static OSStatus
9015 _MDNSReplierAnswerListAdd(
9016 MDNSReplierContext * inContext,
9017 MRResourceRecord ** inAnswerList,
9018 unsigned int inIndex,
9019 const uint8_t * inName,
9020 unsigned int inType,
9021 unsigned int inClass );
9022 static void
9023 _MDNSReplierAnswerListRemovePTR(
9024 MRResourceRecord ** inAnswerListPtr,
9025 const uint8_t * inName,
9026 const uint8_t * inRData );
9027 static OSStatus
9028 _MDNSReplierSendOrDropResponse(
9029 MDNSReplierContext * inContext,
9030 MRResourceRecord * inAnswerList,
9031 sockaddr_ip * inQuerier,
9032 SocketRef inSock,
9033 unsigned int inIndex,
9034 Boolean inUnicast );
9035 static OSStatus
9036 _MDNSReplierCreateResponse(
9037 MDNSReplierContext * inContext,
9038 MRResourceRecord * inAnswerList,
9039 unsigned int inIndex,
9040 uint8_t ** outResponsePtr,
9041 size_t * outResponseLen );
9042 static OSStatus
9043 _MDNSReplierAppendNameToResponse(
9044 DataBuffer * inResponse,
9045 const uint8_t * inName,
9046 MRNameOffsetItem ** inNameOffsetListPtr );
9047 static Boolean
9048 _MDNSReplierServiceTypeMatch(
9049 const MDNSReplierContext * inContext,
9050 const uint8_t * inName,
9051 unsigned int * outTXTSize,
9052 unsigned int * outCount );
9053 static Boolean
9054 _MDNSReplierServiceInstanceNameMatch(
9055 const MDNSReplierContext * inContext,
9056 const uint8_t * inName,
9057 unsigned int * outIndex,
9058 unsigned int * outTXTSize,
9059 unsigned int * outCount );
9060 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
9061 static Boolean
9062 _MDNSReplierHostnameMatch(
9063 const MDNSReplierContext * inContext,
9064 const uint8_t * inName,
9065 unsigned int * outIndex );
9066 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
9067 static OSStatus
9068 _MRResourceRecordCreate(
9069 uint8_t * inName,
9070 uint16_t inType,
9071 uint16_t inClass,
9072 uint32_t inTTL,
9073 uint16_t inRDLength,
9074 uint8_t * inRData,
9075 MRResourceRecord ** outRecord );
9076 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
9077 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
9078 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
9079 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
9080 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
9081
9082 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
9083 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
9084
9085 static void MDNSReplierCmd( void )
9086 {
9087 OSStatus err;
9088 MDNSReplierContext * context;
9089 SocketRef sockV4 = kInvalidSocketRef;
9090 SocketRef sockV6 = kInvalidSocketRef;
9091 const char * ifname;
9092 size_t len;
9093 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
9094 char ifnameBuf[ IF_NAMESIZE + 1 ];
9095
9096 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
9097 require_noerr_quiet( err, exit );
9098
9099 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
9100 require_noerr_quiet( err, exit );
9101
9102 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
9103 require_noerr_quiet( err, exit );
9104
9105 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
9106 require_noerr_quiet( err, exit );
9107
9108 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
9109 require_noerr_quiet( err, exit );
9110
9111 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
9112 require_noerr_quiet( err, exit );
9113
9114 if( gMDNSReplier_Foreground )
9115 {
9116 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
9117 }
9118
9119 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
9120 require_action( context, exit, err = kNoMemoryErr );
9121
9122 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
9123 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
9124 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
9125 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
9126 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
9127 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
9128 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
9129 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
9130 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
9131 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
9132
9133 #if( TARGET_OS_DARWIN )
9134 if( gMDNSReplier_FollowPID )
9135 {
9136 context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
9137 if( err || ( context->followPID < 0 ) )
9138 {
9139 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
9140 goto exit;
9141 }
9142
9143 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
9144 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
9145 require_noerr( err, exit );
9146 dispatch_resume( context->processMonitor );
9147 }
9148 else
9149 {
9150 context->followPID = -1;
9151 }
9152 #endif
9153
9154 if( context->maxDropCount > 0 )
9155 {
9156 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
9157 require_action( context->dropCounters, exit, err = kNoMemoryErr );
9158 }
9159
9160 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
9161 require_action( context->bitmaps, exit, err = kNoMemoryErr );
9162
9163 // Create the base hostname label.
9164
9165 len = strlen( gMDNSReplier_Hostname );
9166 if( context->maxInstanceCount > 1 )
9167 {
9168 unsigned int maxInstanceCount, digitCount;
9169
9170 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
9171 // "-<instance index>" to the base hostname.
9172
9173 maxInstanceCount = context->maxInstanceCount;
9174 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
9175 len += ( 3 + digitCount );
9176 }
9177
9178 if( len <= kDomainLabelLengthMax )
9179 {
9180 uint8_t * dst = &name[ 1 ];
9181 uint8_t * lim = &name[ countof( name ) ];
9182
9183 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
9184 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
9185
9186 err = DomainNameDupLower( name, &context->hostname, NULL );
9187 require_noerr( err, exit );
9188 }
9189 else
9190 {
9191 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
9192 gMDNSReplier_Hostname, context->maxInstanceCount );
9193 goto exit;
9194 }
9195
9196 // Create the service label.
9197
9198 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
9199 if( len <= kDomainLabelLengthMax )
9200 {
9201 uint8_t * dst = &name[ 1 ];
9202 uint8_t * lim = &name[ countof( name ) ];
9203
9204 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
9205 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
9206
9207 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
9208 require_noerr( err, exit );
9209 }
9210 else
9211 {
9212 FPrintF( stderr, "error: Service type tag is too long.\n" );
9213 goto exit;
9214 }
9215
9216 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
9217 require_noerr_quiet( err, exit );
9218
9219 ifname = if_indextoname( context->ifIndex, ifnameBuf );
9220 require_action( ifname, exit, err = kNameErr );
9221
9222 // Set up IPv4 socket.
9223
9224 if( context->useIPv4 )
9225 {
9226 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
9227 require_noerr( err, exit );
9228 }
9229
9230 // Set up IPv6 socket.
9231
9232 if( context->useIPv6 )
9233 {
9234 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
9235 require_noerr( err, exit );
9236 }
9237
9238 // Create dispatch read sources for socket(s).
9239
9240 if( IsValidSocket( sockV4 ) )
9241 {
9242 SocketContext * sockCtx;
9243
9244 err = SocketContextCreate( sockV4, context, &sockCtx );
9245 require_noerr( err, exit );
9246 sockV4 = kInvalidSocketRef;
9247
9248 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9249 &context->readSourceV4 );
9250 if( err ) ForgetSocketContext( &sockCtx );
9251 require_noerr( err, exit );
9252
9253 dispatch_resume( context->readSourceV4 );
9254 }
9255
9256 if( IsValidSocket( sockV6 ) )
9257 {
9258 SocketContext * sockCtx;
9259
9260 err = SocketContextCreate( sockV6, context, &sockCtx );
9261 require_noerr( err, exit );
9262 sockV6 = kInvalidSocketRef;
9263
9264 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9265 &context->readSourceV6 );
9266 if( err ) ForgetSocketContext( &sockCtx );
9267 require_noerr( err, exit );
9268
9269 dispatch_resume( context->readSourceV6 );
9270 }
9271
9272 dispatch_main();
9273
9274 exit:
9275 ForgetSocket( &sockV4 );
9276 ForgetSocket( &sockV6 );
9277 exit( 1 );
9278 }
9279
9280 #if( TARGET_OS_DARWIN )
9281 //===========================================================================================================================
9282 // _MDNSReplierFollowedProcessHandler
9283 //===========================================================================================================================
9284
9285 static void _MDNSReplierFollowedProcessHandler( void *inContext )
9286 {
9287 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
9288
9289 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
9290 {
9291 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
9292 exit( 0 );
9293 }
9294 }
9295 #endif
9296
9297 //===========================================================================================================================
9298 // _MDNSReplierReadHandler
9299 //===========================================================================================================================
9300
9301 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9302
9303 static void _MDNSReplierReadHandler( void *inContext )
9304 {
9305 OSStatus err;
9306 SocketContext * const sockCtx = (SocketContext *) inContext;
9307 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
9308 size_t msgLen;
9309 sockaddr_ip sender;
9310 const DNSHeader * hdr;
9311 unsigned int flags, questionCount, i, j;
9312 const uint8_t * ptr;
9313 int drop, isMetaQuery;
9314
9315 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
9316 NULL, NULL, NULL, NULL );
9317 require_noerr( err, exit );
9318
9319 if( msgLen < kDNSHeaderLength )
9320 {
9321 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
9322 goto exit;
9323 }
9324
9325 // Perform header field checks.
9326 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9327
9328 hdr = (DNSHeader *) context->msgBuf;
9329 flags = DNSHeaderGetFlags( hdr );
9330 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
9331 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
9332 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
9333
9334 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
9335
9336 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9337 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
9338
9339 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9340
9341 questionCount = DNSHeaderGetQuestionCount( hdr );
9342 require_quiet( questionCount > 0, exit );
9343
9344 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
9345
9346 isMetaQuery = false;
9347 ptr = (const uint8_t *) &hdr[ 1 ];
9348 for( i = 0; i < questionCount; ++i )
9349 {
9350 unsigned int count, index;
9351 uint16_t qtype, qclass;
9352 uint8_t qname[ kDomainNameLengthMax ];
9353
9354 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
9355 require_noerr_quiet( err, exit );
9356
9357 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
9358
9359 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
9360 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
9361 {
9362 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
9363 {
9364 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
9365 }
9366 }
9367 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
9368 {
9369 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
9370 {
9371 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
9372 {
9373 if( count < 64 )
9374 {
9375 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
9376 break;
9377 }
9378 else
9379 {
9380 context->bitmaps[ j ] = ~UINT64_C( 0 );
9381 count -= 64;
9382 }
9383 }
9384 }
9385 }
9386 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
9387 {
9388 isMetaQuery = true;
9389 }
9390 }
9391
9392 // Attempt to answer the query message using selected record sets.
9393
9394 if( isMetaQuery )
9395 {
9396 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
9397 check_noerr( err );
9398 }
9399 if( drop ) goto exit;
9400
9401 for( i = 0; i < context->bitmapCount; ++i )
9402 {
9403 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
9404 {
9405 const uint64_t bitmask = UINT64_C( 1 ) << j;
9406
9407 if( context->bitmaps[ i ] & bitmask )
9408 {
9409 context->bitmaps[ i ] &= ~bitmask;
9410
9411 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
9412 ( i * 64 ) + j + 1 );
9413 check_noerr( err );
9414 }
9415 }
9416 }
9417
9418 exit:
9419 return;
9420 }
9421
9422 //===========================================================================================================================
9423 // _MDNSReplierAnswerQuery
9424 //===========================================================================================================================
9425
9426 static OSStatus
9427 _MDNSReplierAnswerQuery(
9428 MDNSReplierContext * inContext,
9429 const uint8_t * inQueryPtr,
9430 size_t inQueryLen,
9431 sockaddr_ip * inSender,
9432 SocketRef inSock,
9433 unsigned int inIndex )
9434 {
9435 OSStatus err;
9436 const DNSHeader * hdr;
9437 const uint8_t * ptr;
9438 unsigned int questionCount, answerCount, i;
9439 MRResourceRecord * ucastAnswerList = NULL;
9440 MRResourceRecord * mcastAnswerList = NULL;
9441
9442 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9443
9444 // Get answers for questions.
9445
9446 check( inQueryLen >= kDNSHeaderLength );
9447 hdr = (const DNSHeader *) inQueryPtr;
9448 questionCount = DNSHeaderGetQuestionCount( hdr );
9449
9450 ptr = (const uint8_t *) &hdr[ 1 ];
9451 for( i = 0; i < questionCount; ++i )
9452 {
9453 MRResourceRecord ** answerListPtr;
9454 uint16_t qtype, qclass;
9455 uint8_t qname[ kDomainNameLengthMax ];
9456
9457 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
9458 require_noerr_quiet( err, exit );
9459
9460 if( qclass & kQClassUnicastResponseBit )
9461 {
9462 qclass &= ~kQClassUnicastResponseBit;
9463 answerListPtr = &ucastAnswerList;
9464 }
9465 else
9466 {
9467 answerListPtr = &mcastAnswerList;
9468 }
9469
9470 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
9471 require_noerr( err, exit );
9472 }
9473 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9474
9475 // Suppress known answers.
9476 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9477 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9478
9479 answerCount = DNSHeaderGetAnswerCount( hdr );
9480 for( i = 0; i < answerCount; ++i )
9481 {
9482 const uint8_t * rdataPtr;
9483 const uint8_t * recordPtr;
9484 uint16_t type, class;
9485 uint8_t name[ kDomainNameLengthMax ];
9486 uint8_t instance[ kDomainNameLengthMax ];
9487
9488 recordPtr = ptr;
9489 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
9490 require_noerr_quiet( err, exit );
9491
9492 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
9493
9494 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
9495 require_noerr( err, exit );
9496
9497 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
9498 require_noerr_quiet( err, exit );
9499
9500 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
9501 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
9502 }
9503 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9504
9505 // Send or drop responses.
9506
9507 if( ucastAnswerList )
9508 {
9509 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
9510 require_noerr( err, exit );
9511 }
9512
9513 if( mcastAnswerList )
9514 {
9515 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
9516 require_noerr( err, exit );
9517 }
9518 err = kNoErr;
9519
9520 exit:
9521 _MRResourceRecordFreeList( ucastAnswerList );
9522 _MRResourceRecordFreeList( mcastAnswerList );
9523 return( err );
9524 }
9525
9526 //===========================================================================================================================
9527 // _MDNSReplierAnswerListAdd
9528 //===========================================================================================================================
9529
9530 static OSStatus
9531 _MDNSReplierAnswerListAdd(
9532 MDNSReplierContext * inContext,
9533 MRResourceRecord ** inAnswerList,
9534 unsigned int inIndex,
9535 const uint8_t * inName,
9536 unsigned int inType,
9537 unsigned int inClass )
9538 {
9539 OSStatus err;
9540 uint8_t * recordName = NULL;
9541 uint8_t * rdataPtr = NULL;
9542 size_t rdataLen;
9543 MRResourceRecord * answer;
9544 MRResourceRecord ** answerPtr;
9545 const uint8_t * const hostname = inContext->hostname;
9546 unsigned int i;
9547 uint32_t index;
9548 unsigned int count, txtSize;
9549
9550 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9551 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
9552
9553 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9554 {
9555 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
9556 {
9557 err = kNoErr;
9558 goto exit;
9559 }
9560 }
9561
9562 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9563 // instance count are for answering queries about service instances.
9564
9565 if( inIndex == 0 )
9566 {
9567 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
9568 {
9569 int listHasTXT = false;
9570
9571 if( inType == kDNSServiceType_ANY )
9572 {
9573 for( answer = *inAnswerList; answer; answer = answer->next )
9574 {
9575 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
9576 {
9577 listHasTXT = true;
9578 break;
9579 }
9580 }
9581 }
9582
9583 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9584 {
9585 err = DomainNameDupLower( inName, &recordName, NULL );
9586 require_noerr( err, exit );
9587
9588 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
9589 require_noerr( err, exit );
9590
9591 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9592 (uint16_t) rdataLen, rdataPtr, &answer );
9593 require_noerr( err, exit );
9594 recordName = NULL;
9595 rdataPtr = NULL;
9596
9597 *answerPtr = answer;
9598 }
9599 else if( inType == kDNSServiceType_NSEC )
9600 {
9601 err = DomainNameDupLower( inName, &recordName, NULL );
9602 require_noerr( err, exit );
9603
9604 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
9605 require_noerr( err, exit );
9606
9607 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9608 (uint16_t) rdataLen, rdataPtr, &answer );
9609 require_noerr( err, exit );
9610 recordName = NULL;
9611 rdataPtr = NULL;
9612
9613 *answerPtr = answer;
9614 }
9615 }
9616 }
9617 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
9618 {
9619 int listHasA = false;
9620 int listHasAAAA = false;
9621
9622 if( inType == kDNSServiceType_ANY )
9623 {
9624 for( answer = *inAnswerList; answer; answer = answer->next )
9625 {
9626 if( answer->type == kDNSServiceType_A )
9627 {
9628 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
9629 }
9630 else if( answer->type == kDNSServiceType_AAAA )
9631 {
9632 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
9633 }
9634 if( listHasA && listHasAAAA ) break;
9635 }
9636 }
9637
9638 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
9639 {
9640 for( i = 1; i <= inContext->recordCountA; ++i )
9641 {
9642 err = DomainNameDupLower( inName, &recordName, NULL );
9643 require_noerr( err, exit );
9644
9645 rdataLen = 4;
9646 rdataPtr = (uint8_t *) malloc( rdataLen );
9647 require_action( rdataPtr, exit, err = kNoMemoryErr );
9648
9649 rdataPtr[ 0 ] = 0;
9650 WriteBig16( &rdataPtr[ 1 ], inIndex );
9651 rdataPtr[ 3 ] = (uint8_t) i;
9652
9653 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9654 (uint16_t) rdataLen, rdataPtr, &answer );
9655 require_noerr( err, exit );
9656 recordName = NULL;
9657 rdataPtr = NULL;
9658
9659 *answerPtr = answer;
9660 answerPtr = &answer->next;
9661 }
9662 }
9663
9664 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
9665 {
9666 for( i = 1; i <= inContext->recordCountAAAA; ++i )
9667 {
9668 err = DomainNameDupLower( inName, &recordName, NULL );
9669 require_noerr( err, exit );
9670
9671 rdataLen = 16;
9672 rdataPtr = (uint8_t *) _memdup( kMDNSReplierBaseAddrV6, rdataLen );
9673 require_action( rdataPtr, exit, err = kNoMemoryErr );
9674
9675 WriteBig16( &rdataPtr[ 12 ], inIndex );
9676 rdataPtr[ 15 ] = (uint8_t) i;
9677
9678 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9679 (uint16_t) rdataLen, rdataPtr, &answer );
9680 require_noerr( err, exit );
9681 recordName = NULL;
9682 rdataPtr = NULL;
9683
9684 *answerPtr = answer;
9685 answerPtr = &answer->next;
9686 }
9687 }
9688 else if( inType == kDNSServiceType_NSEC )
9689 {
9690 err = DomainNameDupLower( inName, &recordName, NULL );
9691 require_noerr( err, exit );
9692
9693 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
9694 {
9695 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
9696 require_noerr( err, exit );
9697 }
9698 else if( inContext->recordCountA > 0 )
9699 {
9700 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
9701 require_noerr( err, exit );
9702 }
9703 else if( inContext->recordCountAAAA > 0 )
9704 {
9705 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
9706 require_noerr( err, exit );
9707 }
9708 else
9709 {
9710 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
9711 require_noerr( err, exit );
9712 }
9713
9714 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9715 (uint16_t) rdataLen, rdataPtr, &answer );
9716 require_noerr( err, exit );
9717 recordName = NULL;
9718 rdataPtr = NULL;
9719
9720 *answerPtr = answer;
9721 }
9722 }
9723 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
9724 {
9725 int listHasPTR = false;
9726
9727 if( inType == kDNSServiceType_ANY )
9728 {
9729 for( answer = *inAnswerList; answer; answer = answer->next )
9730 {
9731 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
9732 {
9733 listHasPTR = true;
9734 break;
9735 }
9736 }
9737 }
9738
9739 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
9740 {
9741 size_t recordNameLen;
9742 uint8_t * ptr;
9743 uint8_t * lim;
9744
9745 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
9746 require_noerr( err, exit );
9747
9748 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
9749 rdataPtr = (uint8_t *) malloc( rdataLen );
9750 require_action( rdataPtr, exit, err = kNoMemoryErr );
9751
9752 lim = &rdataPtr[ rdataLen ];
9753
9754 ptr = &rdataPtr[ 1 ];
9755 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9756 ptr += hostname[ 0 ];
9757 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
9758 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
9759
9760 check( (size_t)( lim - ptr ) >= recordNameLen );
9761 memcpy( ptr, recordName, recordNameLen );
9762 ptr += recordNameLen;
9763
9764 rdataLen = (size_t)( ptr - rdataPtr );
9765
9766 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9767 (uint16_t) rdataLen, rdataPtr, &answer );
9768 require_noerr( err, exit );
9769 recordName = NULL;
9770 rdataPtr = NULL;
9771
9772 *answerPtr = answer;
9773 }
9774 }
9775 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
9776 ( index == inIndex ) && ( count >= inIndex ) )
9777 {
9778 int listHasSRV = false;
9779 int listHasTXT = false;
9780
9781 if( inType == kDNSServiceType_ANY )
9782 {
9783 for( answer = *inAnswerList; answer; answer = answer->next )
9784 {
9785 if( answer->type == kDNSServiceType_SRV )
9786 {
9787 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
9788 }
9789 else if( answer->type == kDNSServiceType_TXT )
9790 {
9791 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
9792 }
9793 if( listHasSRV && listHasTXT ) break;
9794 }
9795 }
9796
9797 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
9798 {
9799 dns_fixed_fields_srv * fields;
9800 uint8_t * ptr;
9801 uint8_t * lim;
9802 uint8_t * targetPtr;
9803
9804 err = DomainNameDupLower( inName, &recordName, NULL );
9805 require_noerr( err, exit );
9806
9807 rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
9808 rdataPtr = (uint8_t *) malloc( rdataLen );
9809 require_action( rdataPtr, exit, err = kNoMemoryErr );
9810
9811 lim = &rdataPtr[ rdataLen ];
9812
9813 fields = (dns_fixed_fields_srv *) rdataPtr;
9814 dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
9815
9816 targetPtr = (uint8_t *) &fields[ 1 ];
9817
9818 ptr = &targetPtr[ 1 ];
9819 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9820 ptr += hostname[ 0 ];
9821 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
9822 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
9823
9824 check( (size_t)( lim - ptr ) >= kLocalNameLen );
9825 memcpy( ptr, kLocalName, kLocalNameLen );
9826 ptr += kLocalNameLen;
9827
9828 rdataLen = (size_t)( ptr - rdataPtr );
9829
9830 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9831 (uint16_t) rdataLen, rdataPtr, &answer );
9832 require_noerr( err, exit );
9833 recordName = NULL;
9834 rdataPtr = NULL;
9835
9836 *answerPtr = answer;
9837 answerPtr = &answer->next;
9838 }
9839
9840 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9841 {
9842 err = DomainNameDupLower( inName, &recordName, NULL );
9843 require_noerr( err, exit );
9844
9845 rdataLen = txtSize;
9846 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
9847 require_noerr( err, exit );
9848
9849 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9850 (uint16_t) rdataLen, rdataPtr, &answer );
9851 require_noerr( err, exit );
9852 recordName = NULL;
9853 rdataPtr = NULL;
9854
9855 *answerPtr = answer;
9856 }
9857 else if( inType == kDNSServiceType_NSEC )
9858 {
9859 err = DomainNameDupLower( inName, &recordName, NULL );
9860 require_noerr( err, exit );
9861
9862 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
9863 require_noerr( err, exit );
9864
9865 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9866 (uint16_t) rdataLen, rdataPtr, &answer );
9867 require_noerr( err, exit );
9868 recordName = NULL;
9869 rdataPtr = NULL;
9870
9871 *answerPtr = answer;
9872 }
9873 }
9874 err = kNoErr;
9875
9876 exit:
9877 FreeNullSafe( recordName );
9878 FreeNullSafe( rdataPtr );
9879 return( err );
9880 }
9881
9882 //===========================================================================================================================
9883 // _MDNSReplierAnswerListRemovePTR
9884 //===========================================================================================================================
9885
9886 static void
9887 _MDNSReplierAnswerListRemovePTR(
9888 MRResourceRecord ** inAnswerListPtr,
9889 const uint8_t * inName,
9890 const uint8_t * inRData )
9891 {
9892 MRResourceRecord * answer;
9893 MRResourceRecord ** answerPtr;
9894
9895 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9896 {
9897 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
9898 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
9899 }
9900 if( answer )
9901 {
9902 *answerPtr = answer->next;
9903 _MRResourceRecordFree( answer );
9904 }
9905 }
9906
9907 //===========================================================================================================================
9908 // _MDNSReplierSendOrDropResponse
9909 //===========================================================================================================================
9910
9911 static OSStatus
9912 _MDNSReplierSendOrDropResponse(
9913 MDNSReplierContext * inContext,
9914 MRResourceRecord * inAnswerList,
9915 sockaddr_ip * inQuerier,
9916 SocketRef inSock,
9917 unsigned int inIndex,
9918 Boolean inUnicast )
9919 {
9920 OSStatus err;
9921 uint8_t * responsePtr = NULL;
9922 size_t responseLen;
9923 const struct sockaddr * destAddr;
9924 ssize_t n;
9925 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
9926 int drop;
9927
9928 check( inIndex <= inContext->maxInstanceCount );
9929
9930 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9931 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9932 // as opposed to those for service instance records, are never dropped.
9933
9934 drop = false;
9935 if( inIndex > 0 )
9936 {
9937 if( inContext->maxDropCount > 0 )
9938 {
9939 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
9940
9941 if( *dropCount < inContext->maxDropCount )
9942 {
9943 if( ShouldDrop( dropRate ) ) drop = true;
9944 *dropCount += 1;
9945 }
9946 }
9947 else if( ShouldDrop( dropRate ) )
9948 {
9949 drop = true;
9950 }
9951 }
9952
9953 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
9954 require_noerr( err, exit );
9955
9956 if( inUnicast )
9957 {
9958 destAddr = &inQuerier->sa;
9959 }
9960 else
9961 {
9962 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9963 }
9964
9965 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9966 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
9967
9968 if( !drop )
9969 {
9970 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
9971 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
9972 require_noerr( err, exit );
9973 }
9974
9975 exit:
9976 FreeNullSafe( responsePtr );
9977 return( err );
9978 }
9979
9980 //===========================================================================================================================
9981 // _MDNSReplierCreateResponse
9982 //===========================================================================================================================
9983
9984 static OSStatus
9985 _MDNSReplierCreateResponse(
9986 MDNSReplierContext * inContext,
9987 MRResourceRecord * inAnswerList,
9988 unsigned int inIndex,
9989 uint8_t ** outResponsePtr,
9990 size_t * outResponseLen )
9991 {
9992 OSStatus err;
9993 DataBuffer responseDB;
9994 DNSHeader hdr;
9995 MRResourceRecord * answer;
9996 uint8_t * responsePtr;
9997 size_t responseLen, len;
9998 unsigned int answerCount, recordCount;
9999 MRNameOffsetItem * nameOffsetList = NULL;
10000
10001 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
10002
10003 // The current answers in the answer list will make up the response's Answer Record Section.
10004
10005 answerCount = 0;
10006 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
10007
10008 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
10009
10010 if( !inContext->noAdditionals )
10011 {
10012 for( answer = inAnswerList; answer; answer = answer->next )
10013 {
10014 switch( answer->type )
10015 {
10016 case kDNSServiceType_PTR:
10017 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
10018 answer->class );
10019 require_noerr( err, exit );
10020
10021 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
10022 answer->class );
10023 require_noerr( err, exit );
10024 break;
10025
10026 case kDNSServiceType_SRV:
10027 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
10028 answer->class );
10029 require_noerr( err, exit );
10030
10031 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
10032 answer->class );
10033 require_noerr( err, exit );
10034
10035 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
10036 answer->class );
10037 require_noerr( err, exit );
10038 break;
10039
10040 case kDNSServiceType_TXT:
10041 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
10042 answer->class );
10043 require_noerr( err, exit );
10044 break;
10045
10046 case kDNSServiceType_A:
10047 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
10048 answer->class );
10049 require_noerr( err, exit );
10050
10051 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
10052 answer->class );
10053 require_noerr( err, exit );
10054 break;
10055
10056 case kDNSServiceType_AAAA:
10057 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
10058 answer->class );
10059 require_noerr( err, exit );
10060
10061 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
10062 answer->class );
10063 require_noerr( err, exit );
10064 break;
10065
10066 default:
10067 break;
10068 }
10069 }
10070 }
10071
10072 // Append a provisional header to the response message.
10073
10074 memset( &hdr, 0, sizeof( hdr ) );
10075 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
10076
10077 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
10078 require_noerr( err, exit );
10079
10080 // Append answers to response message.
10081
10082 responseLen = DataBuffer_GetLen( &responseDB );
10083 recordCount = 0;
10084 for( answer = inAnswerList; answer; answer = answer->next )
10085 {
10086 dns_fixed_fields_record fields;
10087 unsigned int class;
10088
10089 // Append record NAME.
10090
10091 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
10092 require_noerr( err, exit );
10093
10094 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
10095
10096 class = answer->class;
10097 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
10098 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
10099 ( answer->type == kDNSServiceType_NSEC ) )
10100 {
10101 class |= kRRClassCacheFlushBit;
10102 }
10103
10104 dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
10105 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
10106 require_noerr( err, exit );
10107
10108 // Append record RDATA.
10109 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
10110
10111 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
10112 ( answer->type == kDNSServiceType_NSEC ) )
10113 {
10114 size_t rdlength;
10115 uint8_t * rdLengthPtr;
10116 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
10117 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
10118
10119 if( answer->type == kDNSServiceType_PTR )
10120 {
10121 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
10122 require_noerr( err, exit );
10123 }
10124 else if( answer->type == kDNSServiceType_SRV )
10125 {
10126 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
10127
10128 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
10129 require_noerr( err, exit );
10130
10131 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
10132 require_noerr( err, exit );
10133 }
10134 else
10135 {
10136 const size_t nameLen = DomainNameLength( answer->rdata );
10137
10138 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
10139 require_noerr( err, exit );
10140
10141 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
10142
10143 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
10144 require_noerr( err, exit );
10145 }
10146
10147 // Set the actual RDLENGTH, which may be less than the original due to name compression.
10148
10149 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
10150 check( rdlength <= UINT16_MAX );
10151
10152 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
10153 WriteBig16( rdLengthPtr, rdlength );
10154 }
10155 else
10156 {
10157 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
10158 require_noerr( err, exit );
10159 }
10160
10161 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
10162 responseLen = DataBuffer_GetLen( &responseDB );
10163 ++recordCount;
10164 }
10165
10166 // Set the response header's Answer and Additional record counts.
10167 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
10168 // response message to exceed the maximum mDNS message size.
10169
10170 if( recordCount <= answerCount )
10171 {
10172 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
10173 }
10174 else
10175 {
10176 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
10177 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
10178 }
10179
10180 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
10181 require_noerr( err, exit );
10182
10183 if( outResponsePtr ) *outResponsePtr = responsePtr;
10184 if( outResponseLen ) *outResponseLen = responseLen;
10185
10186 exit:
10187 _MRNameOffsetItemFreeList( nameOffsetList );
10188 DataBuffer_Free( &responseDB );
10189 return( err );
10190 }
10191
10192 //===========================================================================================================================
10193 // _MDNSReplierAppendNameToResponse
10194 //===========================================================================================================================
10195
10196 static OSStatus
10197 _MDNSReplierAppendNameToResponse(
10198 DataBuffer * inResponse,
10199 const uint8_t * inName,
10200 MRNameOffsetItem ** inNameOffsetListPtr )
10201 {
10202 OSStatus err;
10203 const uint8_t * subname;
10204 const uint8_t * limit;
10205 size_t nameOffset;
10206 MRNameOffsetItem * item;
10207 uint8_t compressionPtr[ 2 ];
10208
10209 nameOffset = DataBuffer_GetLen( inResponse );
10210
10211 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
10212
10213 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
10214 {
10215 for( item = *inNameOffsetListPtr; item; item = item->next )
10216 {
10217 if( DomainNameEqual( item->name, subname ) ) break;
10218 }
10219
10220 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
10221 // the subname's first label.
10222
10223 if( item )
10224 {
10225 DNSMessageWriteLabelPointer( compressionPtr, item->offset );
10226
10227 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
10228 require_noerr( err, exit );
10229 break;
10230 }
10231 else
10232 {
10233 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
10234 require_noerr( err, exit );
10235 }
10236 }
10237
10238 // 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
10239 // label were appended to the response message, so a root label is needed to terminate the complete name.
10240
10241 if( subname[ 0 ] == 0 )
10242 {
10243 err = DataBuffer_Append( inResponse, "", 1 );
10244 require_noerr( err, exit );
10245 }
10246
10247 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
10248
10249 limit = subname;
10250 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
10251 {
10252 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
10253
10254 if( subnameOffset > kDNSCompressionOffsetMax ) break;
10255
10256 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
10257 require_noerr( err, exit );
10258
10259 item->next = *inNameOffsetListPtr;
10260 *inNameOffsetListPtr = item;
10261 }
10262 err = kNoErr;
10263
10264 exit:
10265 return( err );
10266 }
10267
10268 //===========================================================================================================================
10269 // _MDNSReplierServiceTypeMatch
10270 //===========================================================================================================================
10271
10272 static Boolean
10273 _MDNSReplierServiceTypeMatch(
10274 const MDNSReplierContext * inContext,
10275 const uint8_t * inName,
10276 unsigned int * outTXTSize,
10277 unsigned int * outCount )
10278 {
10279 OSStatus err;
10280 const char * ptr;
10281 const char * end;
10282 uint32_t txtSize, count;
10283 const uint8_t * const serviceLabel = inContext->serviceLabel;
10284 int nameMatches = false;
10285
10286 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
10287 if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
10288
10289 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
10290 end = (const char *) &inName[ 1 + inName[ 0 ] ];
10291
10292 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10293 ++ptr;
10294
10295 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
10296 require_noerr_quiet( err, exit );
10297 require_quiet( txtSize <= UINT16_MAX, exit );
10298
10299 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10300 ++ptr;
10301
10302 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
10303 require_noerr_quiet( err, exit );
10304 require_quiet( count <= UINT16_MAX, exit );
10305 require_quiet( ptr == end, exit );
10306
10307 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
10308 nameMatches = true;
10309
10310 if( outTXTSize ) *outTXTSize = txtSize;
10311 if( outCount ) *outCount = count;
10312
10313 exit:
10314 return( nameMatches ? true : false );
10315 }
10316
10317 //===========================================================================================================================
10318 // _MDNSReplierServiceInstanceNameMatch
10319 //===========================================================================================================================
10320
10321 static Boolean
10322 _MDNSReplierServiceInstanceNameMatch(
10323 const MDNSReplierContext * inContext,
10324 const uint8_t * inName,
10325 unsigned int * outIndex,
10326 unsigned int * outTXTSize,
10327 unsigned int * outCount )
10328 {
10329 OSStatus err;
10330 const uint8_t * ptr;
10331 const uint8_t * end;
10332 uint32_t index;
10333 unsigned int txtSize, count;
10334 const uint8_t * const hostname = inContext->hostname;
10335 int nameMatches = false;
10336
10337 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10338 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10339
10340 ptr = &inName[ 1 + hostname[ 0 ] ];
10341 end = &inName[ 1 + inName[ 0 ] ];
10342 if( ptr < end )
10343 {
10344 require_quiet( ( end - ptr ) >= 2, exit );
10345 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
10346 ptr += 2;
10347
10348 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10349 require_noerr_quiet( err, exit );
10350 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10351
10352 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
10353 ++ptr;
10354 }
10355 else
10356 {
10357 index = 1;
10358 }
10359
10360 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
10361 nameMatches = true;
10362
10363 if( outIndex ) *outIndex = index;
10364 if( outTXTSize ) *outTXTSize = txtSize;
10365 if( outCount ) *outCount = count;
10366
10367 exit:
10368 return( nameMatches ? true : false );
10369 }
10370
10371 //===========================================================================================================================
10372 // _MDNSReplierAboutRecordNameMatch
10373 //===========================================================================================================================
10374
10375 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10376 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10377
10378 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
10379 {
10380 const uint8_t * subname;
10381 const uint8_t * const hostname = inContext->hostname;
10382 int nameMatches = false;
10383
10384 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
10385 subname = DomainNameGetNextLabel( inName );
10386
10387 if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
10388 subname = DomainNameGetNextLabel( subname );
10389
10390 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
10391 nameMatches = true;
10392
10393 exit:
10394 return( nameMatches ? true : false );
10395 }
10396
10397 //===========================================================================================================================
10398 // _MDNSReplierHostnameMatch
10399 //===========================================================================================================================
10400
10401 static Boolean
10402 _MDNSReplierHostnameMatch(
10403 const MDNSReplierContext * inContext,
10404 const uint8_t * inName,
10405 unsigned int * outIndex )
10406 {
10407 OSStatus err;
10408 const uint8_t * ptr;
10409 const uint8_t * end;
10410 uint32_t index;
10411 const uint8_t * const hostname = inContext->hostname;
10412 int nameMatches = false;
10413
10414 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10415 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10416
10417 ptr = &inName[ 1 + hostname[ 0 ] ];
10418 end = &inName[ 1 + inName[ 0 ] ];
10419 if( ptr < end )
10420 {
10421 require_quiet( *ptr == '-', exit );
10422 ++ptr;
10423
10424 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10425 require_noerr_quiet( err, exit );
10426 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10427 require_quiet( ptr == end, exit );
10428 }
10429 else
10430 {
10431 index = 1;
10432 }
10433
10434 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
10435 nameMatches = true;
10436
10437 if( outIndex ) *outIndex = index;
10438
10439 exit:
10440 return( nameMatches ? true : false );
10441 }
10442
10443 //===========================================================================================================================
10444 // _MDNSReplierCreateTXTRecord
10445 //===========================================================================================================================
10446
10447 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
10448 {
10449 OSStatus err;
10450 uint8_t * txt;
10451 uint8_t * ptr;
10452 size_t i, wholeCount, remCount;
10453 uint32_t hash;
10454 int n;
10455 uint8_t txtStr[ 16 ];
10456
10457 require_action_quiet( inSize > 0, exit, err = kSizeErr );
10458
10459 txt = (uint8_t *) malloc( inSize );
10460 require_action( txt, exit, err = kNoMemoryErr );
10461
10462 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
10463
10464 txtStr[ 0 ] = 15;
10465 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
10466 check( n == 15 );
10467
10468 ptr = txt;
10469 wholeCount = inSize / 16;
10470 for( i = 0; i < wholeCount; ++i )
10471 {
10472 memcpy( ptr, txtStr, 16 );
10473 ptr += 16;
10474 }
10475
10476 remCount = inSize % 16;
10477 if( remCount > 0 )
10478 {
10479 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
10480 memcpy( ptr, txtStr, remCount );
10481 ptr += remCount;
10482 }
10483 check( ptr == &txt[ inSize ] );
10484
10485 *outTXT = txt;
10486 err = kNoErr;
10487
10488 exit:
10489 return( err );
10490 }
10491
10492 //===========================================================================================================================
10493 // _MRResourceRecordCreate
10494 //===========================================================================================================================
10495
10496 static OSStatus
10497 _MRResourceRecordCreate(
10498 uint8_t * inName,
10499 uint16_t inType,
10500 uint16_t inClass,
10501 uint32_t inTTL,
10502 uint16_t inRDLength,
10503 uint8_t * inRData,
10504 MRResourceRecord ** outRecord )
10505 {
10506 OSStatus err;
10507 MRResourceRecord * obj;
10508
10509 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
10510 require_action( obj, exit, err = kNoMemoryErr );
10511
10512 obj->name = inName;
10513 obj->type = inType;
10514 obj->class = inClass;
10515 obj->ttl = inTTL;
10516 obj->rdlength = inRDLength;
10517 obj->rdata = inRData;
10518
10519 if( inType == kDNSServiceType_SRV )
10520 {
10521 require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
10522 obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
10523 }
10524
10525 *outRecord = obj;
10526 obj = NULL;
10527 err = kNoErr;
10528
10529 exit:
10530 FreeNullSafe( obj );
10531 return( err );
10532 }
10533
10534 //===========================================================================================================================
10535 // _MRResourceRecordFree
10536 //===========================================================================================================================
10537
10538 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
10539 {
10540 ForgetMem( &inRecord->name );
10541 ForgetMem( &inRecord->rdata );
10542 free( inRecord );
10543 }
10544
10545 //===========================================================================================================================
10546 // _MRResourceRecordFreeList
10547 //===========================================================================================================================
10548
10549 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
10550 {
10551 MRResourceRecord * record;
10552
10553 while( ( record = inList ) != NULL )
10554 {
10555 inList = record->next;
10556 _MRResourceRecordFree( record );
10557 }
10558 }
10559
10560 //===========================================================================================================================
10561 // _MRNameOffsetItemCreate
10562 //===========================================================================================================================
10563
10564 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
10565 {
10566 OSStatus err;
10567 MRNameOffsetItem * obj;
10568 size_t nameLen;
10569
10570 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
10571
10572 nameLen = DomainNameLength( inName );
10573 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
10574 require_action( obj, exit, err = kNoMemoryErr );
10575
10576 obj->offset = inOffset;
10577 memcpy( obj->name, inName, nameLen );
10578
10579 *outItem = obj;
10580 err = kNoErr;
10581
10582 exit:
10583 return( err );
10584 }
10585
10586 //===========================================================================================================================
10587 // _MRNameOffsetItemFree
10588 //===========================================================================================================================
10589
10590 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
10591 {
10592 free( inItem );
10593 }
10594
10595 //===========================================================================================================================
10596 // _MRNameOffsetItemFreeList
10597 //===========================================================================================================================
10598
10599 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
10600 {
10601 MRNameOffsetItem * item;
10602
10603 while( ( item = inList ) != NULL )
10604 {
10605 inList = item->next;
10606 _MRNameOffsetItemFree( item );
10607 }
10608 }
10609
10610 //===========================================================================================================================
10611 // GAIPerfCmd
10612 //===========================================================================================================================
10613
10614 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10615
10616 typedef struct GAITesterPrivate * GAITesterRef;
10617 typedef struct GAITestCase GAITestCase;
10618
10619 typedef struct
10620 {
10621 const char * name; // Domain name that was resolved.
10622 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
10623 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
10624 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
10625 OSStatus error;
10626
10627 } GAITestItemResult;
10628
10629 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
10630 typedef void
10631 ( *GAITesterResultsHandler_f )(
10632 const char * inCaseTitle,
10633 NanoTime64 inCaseStartTime,
10634 NanoTime64 inCaseEndTime,
10635 const GAITestItemResult * inResultArray,
10636 size_t inResultCount,
10637 void * inContext );
10638
10639 typedef unsigned int GAITestAddrType;
10640 #define kGAITestAddrType_None 0
10641 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10642 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10643 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10644
10645 #define GAITestAddrTypeIsValid( X ) \
10646 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10647
10648 typedef struct
10649 {
10650 GAITesterRef tester; // GAI tester object.
10651 CFMutableArrayRef testCaseResults; // Array of test case results.
10652 unsigned int iterTimeLimitMs; // Amount of time to allow each iteration to complete.
10653 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10654 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
10655 unsigned int defaultIterCount; // Default test case iteration count.
10656 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
10657 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
10658 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
10659 OutputFormatType outputFormat; // Format of test results output.
10660 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10661 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
10662 Boolean testFailed; // True if at least one test case iteration failed.
10663
10664 } GAIPerfContext;
10665
10666 static void GAIPerfContextFree( GAIPerfContext *inContext );
10667 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
10668 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
10669 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
10670 static void
10671 GAIPerfResultsHandler(
10672 const char * inCaseTitle,
10673 NanoTime64 inCaseStartTime,
10674 NanoTime64 inCaseEndTime,
10675 const GAITestItemResult * inResultArray,
10676 size_t inResultCount,
10677 void * inContext );
10678 static void GAIPerfSignalHandler( void *inContext );
10679
10680 CFTypeID GAITesterGetTypeID( void );
10681 static OSStatus
10682 GAITesterCreate(
10683 dispatch_queue_t inQueue,
10684 unsigned int inCallDelayMs,
10685 int inServerDelayMs,
10686 int inServerDefaultTTL,
10687 Boolean inSkipPathEvaluation,
10688 Boolean inBadUDPMode,
10689 GAITesterRef * outTester );
10690 static void GAITesterStart( GAITesterRef inTester );
10691 static void GAITesterStop( GAITesterRef inTester );
10692 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
10693 static void
10694 GAITesterSetStopHandler(
10695 GAITesterRef inTester,
10696 GAITesterStopHandler_f inEventHandler,
10697 void * inEventContext );
10698 static void
10699 GAITesterSetResultsHandler(
10700 GAITesterRef inTester,
10701 GAITesterResultsHandler_f inResultsHandler,
10702 void * inResultsContext );
10703
10704 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
10705 static void GAITestCaseFree( GAITestCase *inCase );
10706 static OSStatus
10707 GAITestCaseAddItem(
10708 GAITestCase * inCase,
10709 unsigned int inAliasCount,
10710 unsigned int inAddressCount,
10711 int inTTL,
10712 GAITestAddrType inHasAddrs,
10713 GAITestAddrType inWantAddrs,
10714 unsigned int inTimeLimitMs,
10715 unsigned int inItemCount );
10716 static OSStatus
10717 GAITestCaseAddLocalHostItem(
10718 GAITestCase * inCase,
10719 GAITestAddrType inWantAddrs,
10720 unsigned int inTimeLimitMs,
10721 unsigned int inItemCount );
10722
10723 static void GAIPerfCmd( void )
10724 {
10725 OSStatus err;
10726 GAIPerfContext * context = NULL;
10727
10728 err = CheckRootUser();
10729 require_noerr_quiet( err, exit );
10730
10731 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
10732 require_noerr_quiet( err, exit );
10733
10734 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
10735 require_noerr_quiet( err, exit );
10736
10737 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
10738 require_noerr_quiet( err, exit );
10739
10740 err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
10741 require_noerr_quiet( err, exit );
10742
10743 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
10744 require_action( context, exit, err = kNoMemoryErr );
10745
10746 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
10747 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
10748
10749 context->iterTimeLimitMs = (unsigned int) gGAIPerf_IterationTimeLimitMs;
10750 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
10751 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
10752 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
10753 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
10754 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
10755
10756 if( gGAIPerf_OutputFilePath )
10757 {
10758 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
10759 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
10760 }
10761
10762 err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
10763 require_noerr_quiet( err, exit );
10764
10765 err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
10766 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
10767 require_noerr( err, exit );
10768
10769 check( gGAIPerf_TestSuite );
10770 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
10771 {
10772 err = GAIPerfAddBasicTestCases( context );
10773 require_noerr( err, exit );
10774 }
10775 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
10776 {
10777 err = GAIPerfAddAdvancedTestCases( context );
10778 require_noerr( err, exit );
10779 }
10780 else
10781 {
10782 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
10783 goto exit;
10784 }
10785
10786 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
10787 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
10788
10789 signal( SIGINT, SIG_IGN );
10790 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
10791 &context->sigIntSource );
10792 require_noerr( err, exit );
10793 dispatch_resume( context->sigIntSource );
10794
10795 signal( SIGTERM, SIG_IGN );
10796 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
10797 &context->sigTermSource );
10798 require_noerr( err, exit );
10799 dispatch_resume( context->sigTermSource );
10800
10801 GAITesterStart( context->tester );
10802 dispatch_main();
10803
10804 exit:
10805 if( context ) GAIPerfContextFree( context );
10806 exit( 1 );
10807 }
10808
10809 //===========================================================================================================================
10810 // GAIPerfContextFree
10811 //===========================================================================================================================
10812
10813 static void GAIPerfContextFree( GAIPerfContext *inContext )
10814 {
10815 ForgetCF( &inContext->tester );
10816 ForgetCF( &inContext->testCaseResults );
10817 ForgetMem( &inContext->outputFilePath );
10818 dispatch_source_forget( &inContext->sigIntSource );
10819 dispatch_source_forget( &inContext->sigTermSource );
10820 free( inContext );
10821 }
10822
10823 //===========================================================================================================================
10824 // GAIPerfAddAdvancedTestCases
10825 //===========================================================================================================================
10826
10827 #define kTestCaseTitleBufferSize 128
10828
10829 static void
10830 _GAIPerfWriteTestCaseTitle(
10831 char inBuffer[ kTestCaseTitleBufferSize ],
10832 unsigned int inCNAMERecordCount,
10833 unsigned int inARecordCount,
10834 unsigned int inAAAARecordCount,
10835 GAITestAddrType inRequested,
10836 unsigned int inIterationCount,
10837 Boolean inIterationsAreUnique );
10838 static void
10839 _GAIPerfWriteLocalHostTestCaseTitle(
10840 char inBuffer[ kTestCaseTitleBufferSize ],
10841 GAITestAddrType inRequested,
10842 unsigned int inIterationCount );
10843
10844 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10845 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10846
10847 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
10848 {
10849 OSStatus err;
10850 unsigned int aliasCount, addressCount, i;
10851 GAITestCase * testCase = NULL;
10852 char title[ kTestCaseTitleBufferSize ];
10853
10854 aliasCount = 0;
10855 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
10856 {
10857 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
10858 {
10859 // Add a test case to resolve a domain name with
10860 //
10861 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10862 //
10863 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10864 // requires server queries.
10865
10866 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10867 inContext->defaultIterCount, true );
10868
10869 err = GAITestCaseCreate( title, &testCase );
10870 require_noerr( err, exit );
10871
10872 for( i = 0; i < inContext->defaultIterCount; ++i )
10873 {
10874 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10875 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10876 require_noerr( err, exit );
10877 }
10878
10879 err = GAITesterAddTestCase( inContext->tester, testCase );
10880 require_noerr( err, exit );
10881 testCase = NULL;
10882
10883 // Add a test case to resolve a domain name with
10884 //
10885 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10886 //
10887 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10888 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10889 // ideally require no server queries, i.e., the results should come from the cache.
10890
10891 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10892 inContext->defaultIterCount, false );
10893
10894 err = GAITestCaseCreate( title, &testCase );
10895 require_noerr( err, exit );
10896
10897 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10898 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
10899 require_noerr( err, exit );
10900
10901 err = GAITesterAddTestCase( inContext->tester, testCase );
10902 require_noerr( err, exit );
10903 testCase = NULL;
10904 }
10905
10906 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
10907 }
10908
10909 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10910
10911 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10912
10913 err = GAITestCaseCreate( title, &testCase );
10914 require_noerr( err, exit );
10915
10916 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10917 inContext->defaultIterCount );
10918 require_noerr( err, exit );
10919
10920 err = GAITesterAddTestCase( inContext->tester, testCase );
10921 require_noerr( err, exit );
10922 testCase = NULL;
10923
10924 exit:
10925 if( testCase ) GAITestCaseFree( testCase );
10926 return( err );
10927 }
10928
10929 //===========================================================================================================================
10930 // _GAIPerfWriteTestCaseTitle
10931 //===========================================================================================================================
10932
10933 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10934 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10935 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10936 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10937 "" )
10938
10939 static void
10940 _GAIPerfWriteTestCaseTitle(
10941 char inBuffer[ kTestCaseTitleBufferSize ],
10942 unsigned int inCNAMERecordCount,
10943 unsigned int inARecordCount,
10944 unsigned int inAAAARecordCount,
10945 GAITestAddrType inRequested,
10946 unsigned int inIterationCount,
10947 Boolean inIterationsAreUnique )
10948 {
10949 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10950 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
10951 inIterationCount, inIterationsAreUnique, ",unique" );
10952 }
10953
10954 //===========================================================================================================================
10955 // _GAIPerfWriteLocalHostTestCaseTitle
10956 //===========================================================================================================================
10957
10958 static void
10959 _GAIPerfWriteLocalHostTestCaseTitle(
10960 char inBuffer[ kTestCaseTitleBufferSize ],
10961 GAITestAddrType inRequested,
10962 unsigned int inIterationCount )
10963 {
10964 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
10965 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
10966 }
10967
10968 //===========================================================================================================================
10969 // GAIPerfAddBasicTestCases
10970 //===========================================================================================================================
10971
10972 #define kGAIPerfBasicTestSuite_AliasCount 2
10973 #define kGAIPerfBasicTestSuite_AddrCount 4
10974
10975 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
10976 {
10977 OSStatus err;
10978 GAITestCase * testCase = NULL;
10979 char title[ kTestCaseTitleBufferSize ];
10980 unsigned int i;
10981
10982 // Test Case #1:
10983 // Resolve a domain name with
10984 //
10985 // 2 CNAME records, 4 A records, and 4 AAAA records
10986 //
10987 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10988 // queries.
10989
10990 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10991 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10992 inContext->defaultIterCount, true );
10993
10994 err = GAITestCaseCreate( title, &testCase );
10995 require_noerr( err, exit );
10996
10997 for( i = 0; i < inContext->defaultIterCount; ++i )
10998 {
10999 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
11000 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
11001 require_noerr( err, exit );
11002 }
11003
11004 err = GAITesterAddTestCase( inContext->tester, testCase );
11005 require_noerr( err, exit );
11006 testCase = NULL;
11007
11008 // Test Case #2:
11009 // Resolve a domain name with
11010 //
11011 // 2 CNAME records, 4 A records, and 4 AAAA records
11012 //
11013 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
11014 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
11015 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
11016
11017 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
11018 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
11019 inContext->defaultIterCount, false );
11020
11021 err = GAITestCaseCreate( title, &testCase );
11022 require_noerr( err, exit );
11023
11024 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
11025 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
11026 inContext->defaultIterCount + 1 );
11027 require_noerr( err, exit );
11028
11029 err = GAITesterAddTestCase( inContext->tester, testCase );
11030 require_noerr( err, exit );
11031 testCase = NULL;
11032
11033 // Test Case #3:
11034 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
11035
11036 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
11037
11038 err = GAITestCaseCreate( title, &testCase );
11039 require_noerr( err, exit );
11040
11041 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
11042 inContext->defaultIterCount );
11043 require_noerr( err, exit );
11044
11045 err = GAITesterAddTestCase( inContext->tester, testCase );
11046 require_noerr( err, exit );
11047 testCase = NULL;
11048
11049 exit:
11050 if( testCase ) GAITestCaseFree( testCase );
11051 return( err );
11052 }
11053
11054 //===========================================================================================================================
11055 // GAIPerfTesterStopHandler
11056 //===========================================================================================================================
11057
11058 #define kGAIPerfResultsKey_Info CFSTR( "info" )
11059 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
11060 #define kGAIPerfResultsKey_Success CFSTR( "success" )
11061
11062 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
11063 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
11064 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
11065 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
11066
11067 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
11068 {
11069 OSStatus err;
11070 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11071 CFPropertyListRef plist;
11072 int exitCode;
11073
11074 err = inError;
11075 require_noerr_quiet( err, exit );
11076
11077 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
11078 "{"
11079 "%kO=" // info
11080 "{"
11081 "%kO=%lli" // callDelayMs
11082 "%kO=%lli" // serverDelayMs
11083 "%kO=%b" // skippedPathEval
11084 "%kO=%b" // usedBadUPDMode
11085 "}"
11086 "%kO=%O" // testCases
11087 "%kO=%b" // success
11088 "}",
11089 kGAIPerfResultsKey_Info,
11090 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
11091 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
11092 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
11093 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
11094 kGAIPerfResultsKey_TestCases, context->testCaseResults,
11095 kGAIPerfResultsKey_Success, !context->testFailed );
11096 require_noerr( err, exit );
11097
11098 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
11099 CFRelease( plist );
11100 require_noerr( err, exit );
11101
11102 exit:
11103 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
11104 GAIPerfContextFree( context );
11105 exit( exitCode );
11106 }
11107
11108 //===========================================================================================================================
11109 // GAIPerfResultsHandler
11110 //===========================================================================================================================
11111
11112 // Keys for test case dictionary
11113
11114 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
11115 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
11116 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
11117 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
11118 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
11119 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
11120 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
11121
11122 // Keys for test case results array entry dictionaries
11123
11124 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
11125 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
11126 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
11127 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
11128
11129 // Keys for test case stats dictionaries
11130
11131 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
11132 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
11133 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
11134 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
11135 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
11136
11137 typedef struct
11138 {
11139 double min;
11140 double max;
11141 double mean;
11142 double stdDev;
11143
11144 } GAIPerfStats;
11145
11146 #define GAIPerfStatsInit( X ) \
11147 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
11148
11149 static void
11150 GAIPerfResultsHandler(
11151 const char * inCaseTitle,
11152 NanoTime64 inCaseStartTime,
11153 NanoTime64 inCaseEndTime,
11154 const GAITestItemResult * inResultArray,
11155 size_t inResultCount,
11156 void * inContext )
11157 {
11158 OSStatus err;
11159 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11160 int namesAreDynamic, namesAreUnique;
11161 const char * ptr;
11162 size_t count, startIndex;
11163 CFMutableArrayRef results = NULL;
11164 GAIPerfStats stats, firstStats, connStats;
11165 double sum, firstSum, connSum;
11166 size_t keyValueLen, i;
11167 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
11168 char startTime[ 32 ];
11169 char endTime[ 32 ];
11170 const GAITestItemResult * result;
11171
11172 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
11173 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
11174 // the domain name's CNAME, A, and AAAA records.
11175
11176 namesAreDynamic = false;
11177 namesAreUnique = false;
11178 ptr = inCaseTitle;
11179 while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
11180 {
11181 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
11182 {
11183 namesAreDynamic = true;
11184 }
11185 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
11186 {
11187 namesAreUnique = true;
11188 }
11189 if( namesAreDynamic && namesAreUnique ) break;
11190 }
11191
11192 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
11193 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
11194 require_action( results, exit, err = kNoMemoryErr );
11195
11196 GAIPerfStatsInit( &stats );
11197 GAIPerfStatsInit( &firstStats );
11198 GAIPerfStatsInit( &connStats );
11199
11200 sum = 0.0;
11201 firstSum = 0.0;
11202 connSum = 0.0;
11203 count = 0;
11204 for( i = startIndex; i < inResultCount; ++i )
11205 {
11206 double value;
11207
11208 result = &inResultArray[ i ];
11209
11210 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
11211 "{"
11212 "%kO=%s" // name
11213 "%kO=%lli" // connectionTimeUs
11214 "%kO=%lli" // firstTimeUs
11215 "%kO=%lli" // timeUs
11216 "%kO=%lli" // error
11217 "}",
11218 kGAIPerfTestCaseResultKey_Name, result->name,
11219 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
11220 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
11221 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
11222 CFSTR( "error" ), (int64_t) result->error );
11223 require_noerr( err, exit );
11224
11225 if( !result->error )
11226 {
11227 value = (double) result->timeUs;
11228 if( value < stats.min ) stats.min = value;
11229 if( value > stats.max ) stats.max = value;
11230 sum += value;
11231
11232 value = (double) result->firstTimeUs;
11233 if( value < firstStats.min ) firstStats.min = value;
11234 if( value > firstStats.max ) firstStats.max = value;
11235 firstSum += value;
11236
11237 value = (double) result->connectionTimeUs;
11238 if( value < connStats.min ) connStats.min = value;
11239 if( value > connStats.max ) connStats.max = value;
11240 connSum += value;
11241
11242 ++count;
11243 }
11244 else
11245 {
11246 context->testFailed = true;
11247 }
11248 }
11249
11250 if( count > 0 )
11251 {
11252 stats.mean = sum / count;
11253 firstStats.mean = firstSum / count;
11254 connStats.mean = connSum / count;
11255
11256 sum = 0.0;
11257 firstSum = 0.0;
11258 connSum = 0.0;
11259 for( i = startIndex; i < inResultCount; ++i )
11260 {
11261 double diff;
11262
11263 result = &inResultArray[ i ];
11264 if( result->error ) continue;
11265
11266 diff = stats.mean - (double) result->timeUs;
11267 sum += ( diff * diff );
11268
11269 diff = firstStats.mean - (double) result->firstTimeUs;
11270 firstSum += ( diff * diff );
11271
11272 diff = connStats.mean - (double) result->connectionTimeUs;
11273 connSum += ( diff * diff );
11274 }
11275 stats.stdDev = sqrt( sum / count );
11276 firstStats.stdDev = sqrt( firstSum / count );
11277 connStats.stdDev = sqrt( connSum / count );
11278 }
11279
11280 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
11281 "{"
11282 "%kO=%s"
11283 "%kO=%s"
11284 "%kO=%s"
11285 "%kO=%O"
11286 "%kO="
11287 "{"
11288 "%kO=%lli"
11289 "%kO=%f"
11290 "%kO=%f"
11291 "%kO=%f"
11292 "%kO=%f"
11293 "}"
11294 "%kO="
11295 "{"
11296 "%kO=%lli"
11297 "%kO=%f"
11298 "%kO=%f"
11299 "%kO=%f"
11300 "%kO=%f"
11301 "}"
11302 "%kO="
11303 "{"
11304 "%kO=%lli"
11305 "%kO=%f"
11306 "%kO=%f"
11307 "%kO=%f"
11308 "%kO=%f"
11309 "}"
11310 "}",
11311 kGAIPerfTestCaseKey_Title, inCaseTitle,
11312 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
11313 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
11314 kGAIPerfTestCaseKey_Results, results,
11315 kGAIPerfTestCaseKey_Stats,
11316 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11317 kGAIPerfTestCaseStatsKey_Min, stats.min,
11318 kGAIPerfTestCaseStatsKey_Max, stats.max,
11319 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
11320 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
11321 kGAIPerfTestCaseKey_FirstStats,
11322 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11323 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
11324 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
11325 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
11326 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
11327 kGAIPerfTestCaseKey_ConnectionStats,
11328 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11329 kGAIPerfTestCaseStatsKey_Min, connStats.min,
11330 kGAIPerfTestCaseStatsKey_Max, connStats.max,
11331 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
11332 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
11333 require_noerr( err, exit );
11334
11335 exit:
11336 CFReleaseNullSafe( results );
11337 if( err ) exit( 1 );
11338 }
11339
11340 //===========================================================================================================================
11341 // GAIPerfSignalHandler
11342 //===========================================================================================================================
11343
11344 static void GAIPerfSignalHandler( void *inContext )
11345 {
11346 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11347
11348 if( !context->tester ) exit( 1 );
11349 GAITesterStop( context->tester );
11350 context->tester = NULL;
11351 }
11352
11353 //===========================================================================================================================
11354 // GAITesterCreate
11355 //===========================================================================================================================
11356
11357 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11358 // possible strings to use in the Tag label.
11359
11360 #define kGAITesterTagStringLen 6
11361
11362 typedef struct GAITestItem GAITestItem;
11363 struct GAITestItem
11364 {
11365 GAITestItem * next; // Next test item in list.
11366 char * name; // Domain name to resolve.
11367 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
11368 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
11369 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
11370 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
11371 OSStatus error; // Current status/error.
11372 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
11373 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
11374 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
11375 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11376 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11377 };
11378
11379 struct GAITestCase
11380 {
11381 GAITestCase * next; // Next test case in list.
11382 GAITestItem * itemList; // List of test items.
11383 char * title; // Title of the test case.
11384 };
11385
11386 struct GAITesterPrivate
11387 {
11388 CFRuntimeBase base; // CF object base.
11389 dispatch_queue_t queue; // Serial work queue.
11390 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
11391 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
11392 GAITestCase * caseList; // List of test cases.
11393 GAITestCase * currentCase; // Pointer to the current test case.
11394 GAITestItem * currentItem; // Pointer to the current test item.
11395 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
11396 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
11397 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11398 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11399 Boolean stopped; // True if the tester has been stopped.
11400 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
11401 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
11402 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
11403 pid_t serverPID; // PID of the test DNS server.
11404 int serverDelayMs; // Additional time to have the server delay its responses by.
11405 int serverDefaultTTL; // Default TTL for the server's records.
11406 GAITesterStopHandler_f stopHandler; // User's stop handler.
11407 void * stopContext; // User's event handler context.
11408 GAITesterResultsHandler_f resultsHandler; // User's results handler.
11409 void * resultsContext; // User's results handler context.
11410
11411 // Variables for current test item.
11412
11413 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
11414 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
11415 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
11416 uint64_t connTicks; // Ticks when the connection was created.
11417 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
11418 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
11419 Boolean gotFirstResult; // True if the first result has been received.
11420 };
11421
11422 CF_CLASS_DEFINE( GAITester );
11423
11424 static void _GAITesterStartNextTest( GAITesterRef inTester );
11425 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
11426 static void _GAITesterFirstGAITimeout( void *inContext );
11427 static void _GAITesterTimeout( void *inContext );
11428 static void DNSSD_API
11429 _GAITesterFirstGAICallback(
11430 DNSServiceRef inSDRef,
11431 DNSServiceFlags inFlags,
11432 uint32_t inInterfaceIndex,
11433 DNSServiceErrorType inError,
11434 const char * inHostname,
11435 const struct sockaddr * inSockAddr,
11436 uint32_t inTTL,
11437 void * inContext );
11438 static void DNSSD_API
11439 _GAITesterGetAddrInfoCallback(
11440 DNSServiceRef inSDRef,
11441 DNSServiceFlags inFlags,
11442 uint32_t inInterfaceIndex,
11443 DNSServiceErrorType inError,
11444 const char * inHostname,
11445 const struct sockaddr * inSockAddr,
11446 uint32_t inTTL,
11447 void * inContext );
11448 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
11449
11450 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11451
11452 static OSStatus
11453 GAITestItemCreate(
11454 const char * inName,
11455 unsigned int inAddressCount,
11456 GAITestAddrType inHasAddrs,
11457 GAITestAddrType inWantAddrs,
11458 unsigned int inTimeLimitMs,
11459 GAITestItem ** outItem );
11460 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
11461 static void GAITestItemFree( GAITestItem *inItem );
11462
11463 static OSStatus
11464 GAITesterCreate(
11465 dispatch_queue_t inQueue,
11466 unsigned int inCallDelayMs,
11467 int inServerDelayMs,
11468 int inServerDefaultTTL,
11469 Boolean inSkipPathEvaluation,
11470 Boolean inBadUDPMode,
11471 GAITesterRef * outTester )
11472 {
11473 OSStatus err;
11474 GAITesterRef obj = NULL;
11475
11476 CF_OBJECT_CREATE( GAITester, obj, err, exit );
11477
11478 ReplaceDispatchQueue( &obj->queue, inQueue );
11479 obj->callDelayMs = inCallDelayMs;
11480 obj->serverPID = -1;
11481 obj->serverDelayMs = inServerDelayMs;
11482 obj->serverDefaultTTL = inServerDefaultTTL;
11483 obj->skipPathEval = inSkipPathEvaluation;
11484 obj->badUDPMode = inBadUDPMode;
11485
11486 *outTester = obj;
11487 obj = NULL;
11488 err = kNoErr;
11489
11490 exit:
11491 CFReleaseNullSafe( obj );
11492 return( err );
11493 }
11494
11495 //===========================================================================================================================
11496 // _GAITesterFinalize
11497 //===========================================================================================================================
11498
11499 static void _GAITesterFinalize( CFTypeRef inObj )
11500 {
11501 GAITesterRef const me = (GAITesterRef) inObj;
11502 GAITestCase * testCase;
11503
11504 check( !me->getAddrInfo );
11505 check( !me->connection );
11506 check( !me->timer );
11507 dispatch_forget( &me->queue );
11508 while( ( testCase = me->caseList ) != NULL )
11509 {
11510 me->caseList = testCase->next;
11511 GAITestCaseFree( testCase );
11512 }
11513 }
11514
11515 //===========================================================================================================================
11516 // GAITesterStart
11517 //===========================================================================================================================
11518
11519 static void _GAITesterStart( void *inContext );
11520 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
11521
11522 static void GAITesterStart( GAITesterRef me )
11523 {
11524 CFRetain( me );
11525 dispatch_async_f( me->queue, me, _GAITesterStart );
11526 }
11527
11528 #define kGAITesterFirstGAITimeoutSecs 4
11529
11530 static void _GAITesterStart( void *inContext )
11531 {
11532 OSStatus err;
11533 GAITesterRef const me = (GAITesterRef) inContext;
11534 DNSServiceFlags flags;
11535 char name[ 64 ];
11536 char tag[ kGAITesterTagStringLen + 1 ];
11537
11538 err = SpawnCommand( &me->serverPID, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11539 (int64_t) getpid(),
11540 me->serverDefaultTTL >= 0, " --defaultTTL ",
11541 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
11542 me->serverDelayMs >= 0, " --responseDelay ",
11543 me->serverDelayMs >= 0, me->serverDelayMs,
11544 me->badUDPMode, " --badUDPMode" );
11545 require_noerr_quiet( err, exit );
11546
11547 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test",
11548 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
11549
11550 flags = 0;
11551 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11552
11553 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
11554 _GAITesterFirstGAICallback, me );
11555 require_noerr( err, exit );
11556
11557 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
11558 require_noerr( err, exit );
11559
11560 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
11561 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
11562 _GAITesterFirstGAITimeout, me, &me->timer );
11563 require_noerr( err, exit );
11564 dispatch_resume( me->timer );
11565
11566 exit:
11567 if( err ) _GAITesterStop( me, err );
11568 }
11569
11570 //===========================================================================================================================
11571 // GAITesterStop
11572 //===========================================================================================================================
11573
11574 static void _GAITesterUserStop( void *inContext );
11575
11576 static void GAITesterStop( GAITesterRef me )
11577 {
11578 CFRetain( me );
11579 dispatch_async_f( me->queue, me, _GAITesterUserStop );
11580 }
11581
11582 static void _GAITesterUserStop( void *inContext )
11583 {
11584 GAITesterRef const me = (GAITesterRef) inContext;
11585
11586 _GAITesterStop( me, kCanceledErr );
11587 CFRelease( me );
11588 }
11589
11590 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
11591 {
11592 OSStatus err;
11593
11594 ForgetPacketCapture( &me->pcap );
11595 dispatch_source_forget( &me->timer );
11596 DNSServiceForget( &me->getAddrInfo );
11597 DNSServiceForget( &me->connection );
11598 if( me->serverPID != -1 )
11599 {
11600 err = kill( me->serverPID, SIGTERM );
11601 err = map_global_noerr_errno( err );
11602 check_noerr( err );
11603 me->serverPID = -1;
11604 }
11605
11606 if( !me->stopped )
11607 {
11608 me->stopped = true;
11609 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
11610 CFRelease( me );
11611 }
11612 }
11613
11614 //===========================================================================================================================
11615 // GAITesterAddTestCase
11616 //===========================================================================================================================
11617
11618 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
11619 {
11620 OSStatus err;
11621 GAITestCase ** ptr;
11622
11623 require_action_quiet( inCase->itemList, exit, err = kCountErr );
11624
11625 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
11626 *ptr = inCase;
11627 err = kNoErr;
11628
11629 exit:
11630 return( err );
11631 }
11632
11633 //===========================================================================================================================
11634 // GAITesterSetStopHandler
11635 //===========================================================================================================================
11636
11637 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
11638 {
11639 me->stopHandler = inStopHandler;
11640 me->stopContext = inStopContext;
11641 }
11642
11643 //===========================================================================================================================
11644 // GAITesterSetResultsHandler
11645 //===========================================================================================================================
11646
11647 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
11648 {
11649 me->resultsHandler = inResultsHandler;
11650 me->resultsContext = inResultsContext;
11651 }
11652
11653 //===========================================================================================================================
11654 // _GAITesterStartNextTest
11655 //===========================================================================================================================
11656
11657 static void _GAITesterStartNextTest( GAITesterRef me )
11658 {
11659 OSStatus err;
11660 GAITestItem * item;
11661 DNSServiceFlags flags;
11662 DNSServiceProtocol protocols;
11663 int done = false;
11664
11665 if( me->currentItem ) me->currentItem = me->currentItem->next;
11666
11667 if( !me->currentItem )
11668 {
11669 if( me->currentCase )
11670 {
11671 // No more test items means that the current test case has completed.
11672
11673 me->caseEndTime = NanoTimeGetCurrent();
11674
11675 if( me->resultsHandler )
11676 {
11677 size_t resultCount, i;
11678 GAITestItemResult * resultArray;
11679
11680 resultCount = 0;
11681 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
11682 check( resultCount > 0 );
11683
11684 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
11685 require_action( resultArray, exit, err = kNoMemoryErr );
11686
11687 item = me->currentCase->itemList;
11688 for( i = 0; i < resultCount; ++i )
11689 {
11690 resultArray[ i ].name = item->name;
11691 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
11692 resultArray[ i ].firstTimeUs = item->firstTimeUs;
11693 resultArray[ i ].timeUs = item->timeUs;
11694 resultArray[ i ].error = item->error;
11695 item = item->next;
11696 }
11697 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
11698 me->resultsContext );
11699 ForgetMem( &resultArray );
11700 }
11701
11702 me->currentCase = me->currentCase->next;
11703 if( !me->currentCase )
11704 {
11705 done = true;
11706 err = kNoErr;
11707 goto exit;
11708 }
11709 }
11710 else
11711 {
11712 me->currentCase = me->caseList;
11713 }
11714 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
11715 me->currentItem = me->currentCase->itemList;
11716 }
11717
11718 item = me->currentItem;
11719 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
11720
11721 if( !item->wantV4 ) me->bitmapV4 = 0;
11722 else if( !item->hasV4 ) me->bitmapV4 = 1;
11723 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11724 else me->bitmapV4 = ~UINT64_C( 0 );
11725
11726 if( !item->wantV6 ) me->bitmapV6 = 0;
11727 else if( !item->hasV6 ) me->bitmapV6 = 1;
11728 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11729 else me->bitmapV6 = ~UINT64_C( 0 );
11730 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
11731 me->gotFirstResult = false;
11732
11733 // Perform preliminary tasks if this is the start of a new test case.
11734
11735 if( item == me->currentCase->itemList )
11736 {
11737 // Flush mDNSResponder's cache.
11738
11739 err = systemf( NULL, "killall -HUP mDNSResponder" );
11740 require_noerr( err, exit );
11741 sleep( 1 );
11742
11743 me->caseStartTime = NanoTimeGetCurrent();
11744 me->caseEndTime = kNanoTime_Invalid;
11745 }
11746
11747 // Start a packet capture.
11748
11749 check( !me->pcap );
11750 err = _GAITesterCreatePacketCapture( &me->pcap );
11751 require_noerr( err, exit );
11752
11753 // Start timer for test item's time limit.
11754
11755 check( !me->timer );
11756 if( item->timeLimitMs > 0 )
11757 {
11758 unsigned int timeLimitMs;
11759
11760 timeLimitMs = item->timeLimitMs;
11761 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
11762 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
11763
11764 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
11765 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
11766 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
11767 require_noerr( err, exit );
11768 dispatch_resume( me->timer );
11769 }
11770
11771 // Call DNSServiceGetAddrInfo().
11772
11773 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
11774
11775 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
11776 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11777
11778 protocols = 0;
11779 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
11780 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
11781
11782 me->startTicks = UpTicks();
11783
11784 check( !me->connection );
11785 err = DNSServiceCreateConnection( &me->connection );
11786 require_noerr( err, exit );
11787
11788 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
11789 require_noerr( err, exit );
11790
11791 me->connTicks = UpTicks();
11792
11793 check( !me->getAddrInfo );
11794 me->getAddrInfo = me->connection;
11795 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
11796 _GAITesterGetAddrInfoCallback, me );
11797 require_noerr( err, exit );
11798
11799 exit:
11800 if( err || done ) _GAITesterStop( me, err );
11801 }
11802
11803 //===========================================================================================================================
11804 // _GAITesterCreatePacketCapture
11805 //===========================================================================================================================
11806
11807 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
11808 {
11809 OSStatus err;
11810 pcap_t * pcap;
11811 struct bpf_program program;
11812 char errBuf[ PCAP_ERRBUF_SIZE ];
11813
11814 pcap = pcap_create( "lo0", errBuf );
11815 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
11816
11817 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
11818 require_noerr_action( err, exit, err = kUnknownErr );
11819
11820 err = pcap_set_snaplen( pcap, 512 );
11821 require_noerr_action( err, exit, err = kUnknownErr );
11822
11823 err = pcap_set_immediate_mode( pcap, 0 );
11824 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11825
11826 err = pcap_activate( pcap );
11827 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11828
11829 err = pcap_setdirection( pcap, PCAP_D_INOUT );
11830 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11831
11832 err = pcap_setnonblock( pcap, 1, errBuf );
11833 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
11834
11835 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
11836 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11837
11838 err = pcap_setfilter( pcap, &program );
11839 pcap_freecode( &program );
11840 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11841
11842 *outPCap = pcap;
11843 pcap = NULL;
11844
11845 exit:
11846 if( pcap ) pcap_close( pcap );
11847 return( err );
11848 }
11849
11850 //===========================================================================================================================
11851 // _GAITesterFirstGAITimeout
11852 //===========================================================================================================================
11853
11854 static void _GAITesterFirstGAITimeout( void *inContext )
11855 {
11856 GAITesterRef const me = (GAITesterRef) inContext;
11857
11858 _GAITesterStop( me, kNoResourcesErr );
11859 }
11860
11861 //===========================================================================================================================
11862 // _GAITesterTimeout
11863 //===========================================================================================================================
11864
11865 static void _GAITesterTimeout( void *inContext )
11866 {
11867 GAITesterRef const me = (GAITesterRef) inContext;
11868
11869 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
11870 }
11871
11872 //===========================================================================================================================
11873 // _GAITesterFirstGAICallback
11874 //===========================================================================================================================
11875
11876 static void DNSSD_API
11877 _GAITesterFirstGAICallback(
11878 DNSServiceRef inSDRef,
11879 DNSServiceFlags inFlags,
11880 uint32_t inInterfaceIndex,
11881 DNSServiceErrorType inError,
11882 const char * inHostname,
11883 const struct sockaddr * inSockAddr,
11884 uint32_t inTTL,
11885 void * inContext )
11886 {
11887 GAITesterRef const me = (GAITesterRef) inContext;
11888
11889 Unused( inSDRef );
11890 Unused( inInterfaceIndex );
11891 Unused( inHostname );
11892 Unused( inSockAddr );
11893 Unused( inTTL );
11894
11895 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
11896 {
11897 dispatch_source_forget( &me->timer );
11898 DNSServiceForget( &me->getAddrInfo );
11899
11900 _GAITesterStartNextTest( me );
11901 }
11902 }
11903
11904 //===========================================================================================================================
11905 // _GAITesterGetAddrInfoCallback
11906 //===========================================================================================================================
11907
11908 static void DNSSD_API
11909 _GAITesterGetAddrInfoCallback(
11910 DNSServiceRef inSDRef,
11911 DNSServiceFlags inFlags,
11912 uint32_t inInterfaceIndex,
11913 DNSServiceErrorType inError,
11914 const char * inHostname,
11915 const struct sockaddr * inSockAddr,
11916 uint32_t inTTL,
11917 void * inContext )
11918 {
11919 OSStatus err;
11920 GAITesterRef const me = (GAITesterRef) inContext;
11921 GAITestItem * const item = me->currentItem;
11922 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
11923 uint64_t nowTicks;
11924 uint64_t * bitmapPtr;
11925 uint64_t bitmask;
11926 int hasAddr;
11927
11928 Unused( inSDRef );
11929 Unused( inInterfaceIndex );
11930 Unused( inHostname );
11931 Unused( inTTL );
11932
11933 nowTicks = UpTicks();
11934
11935 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
11936
11937 // Check if we were expecting an IP address result of this type.
11938
11939 if( sip->sa.sa_family == AF_INET )
11940 {
11941 bitmapPtr = &me->bitmapV4;
11942 hasAddr = item->hasV4;
11943 }
11944 else if( sip->sa.sa_family == AF_INET6 )
11945 {
11946 bitmapPtr = &me->bitmapV6;
11947 hasAddr = item->hasV6;
11948 }
11949 else
11950 {
11951 err = kTypeErr;
11952 goto exit;
11953 }
11954
11955 bitmask = 0;
11956 if( hasAddr )
11957 {
11958 uint32_t addrOffset;
11959
11960 require_noerr_action_quiet( inError, exit, err = inError );
11961
11962 if( sip->sa.sa_family == AF_INET )
11963 {
11964 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
11965
11966 if( strcasecmp( item->name, "localhost." ) == 0 )
11967 {
11968 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
11969 }
11970 else
11971 {
11972 addrOffset = addrV4 - kDNSServerBaseAddrV4;
11973 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11974 {
11975 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11976 }
11977 }
11978 }
11979 else
11980 {
11981 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
11982
11983 if( strcasecmp( item->name, "localhost." ) == 0 )
11984 {
11985 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
11986 }
11987 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
11988 {
11989 addrOffset = addrV6[ 15 ];
11990 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11991 {
11992 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11993 }
11994 }
11995 }
11996 }
11997 else
11998 {
11999 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
12000 bitmask = 1;
12001 }
12002 require_action_quiet( bitmask != 0, exit, err = kValueErr );
12003 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
12004
12005 *bitmapPtr &= ~bitmask;
12006 if( !me->gotFirstResult )
12007 {
12008 me->firstTicks = nowTicks;
12009 me->gotFirstResult = true;
12010 }
12011 err = kNoErr;
12012
12013 exit:
12014 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
12015 {
12016 me->endTicks = nowTicks;
12017 _GAITesterCompleteCurrentTest( me, err );
12018 }
12019 }
12020
12021 //===========================================================================================================================
12022 // _GAITesterCompleteCurrentTest
12023 //===========================================================================================================================
12024
12025 static OSStatus
12026 _GAITesterGetDNSMessageFromPacket(
12027 const uint8_t * inPacketPtr,
12028 size_t inPacketLen,
12029 const uint8_t ** outMsgPtr,
12030 size_t * outMsgLen );
12031
12032 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
12033 {
12034 OSStatus err;
12035 GAITestItem * const item = me->currentItem;
12036 struct timeval timeStamps[ 4 ];
12037 struct timeval * tsPtr;
12038 struct timeval * tsQA = NULL;
12039 struct timeval * tsQAAAA = NULL;
12040 struct timeval * tsRA = NULL;
12041 struct timeval * tsRAAAA = NULL;
12042 struct timeval * t1;
12043 struct timeval * t2;
12044 int64_t idleTimeUs;
12045 uint8_t name[ kDomainNameLengthMax ];
12046
12047 dispatch_source_forget( &me->timer );
12048 DNSServiceForget( &me->getAddrInfo );
12049 DNSServiceForget( &me->connection );
12050
12051 item->error = inError;
12052 if( item->error )
12053 {
12054 err = kNoErr;
12055 goto exit;
12056 }
12057
12058 err = DomainNameFromString( name, item->name, NULL );
12059 require_noerr( err, exit );
12060
12061 tsPtr = &timeStamps[ 0 ];
12062 for( ;; )
12063 {
12064 int status;
12065 struct pcap_pkthdr * pktHdr;
12066 const uint8_t * packet;
12067 const uint8_t * msgPtr;
12068 size_t msgLen;
12069 const DNSHeader * hdr;
12070 unsigned int flags;
12071 const uint8_t * ptr;
12072 uint16_t qtype, qclass;
12073 uint8_t qname[ kDomainNameLengthMax ];
12074
12075 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
12076 if( status != 1 ) break;
12077 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
12078 if( msgLen < kDNSHeaderLength ) continue;
12079
12080 hdr = (const DNSHeader *) msgPtr;
12081 flags = DNSHeaderGetFlags( hdr );
12082 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
12083 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
12084
12085 ptr = (const uint8_t *) &hdr[ 1 ];
12086 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
12087 if( qclass != kDNSServiceClass_IN ) continue;
12088 if( !DomainNameEqual( qname, name ) ) continue;
12089
12090 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
12091 {
12092 if( flags & kDNSHeaderFlag_Response )
12093 {
12094 if( tsQA && !tsRA )
12095 {
12096 tsRA = tsPtr++;
12097 *tsRA = pktHdr->ts;
12098 }
12099 }
12100 else if( !tsQA )
12101 {
12102 tsQA = tsPtr++;
12103 *tsQA = pktHdr->ts;
12104 }
12105 }
12106 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
12107 {
12108 if( flags & kDNSHeaderFlag_Response )
12109 {
12110 if( tsQAAAA && !tsRAAAA )
12111 {
12112 tsRAAAA = tsPtr++;
12113 *tsRAAAA = pktHdr->ts;
12114 }
12115 }
12116 else if( !tsQAAAA )
12117 {
12118 tsQAAAA = tsPtr++;
12119 *tsQAAAA = pktHdr->ts;
12120 }
12121 }
12122 }
12123
12124 // t1 is the time when the last query was sent.
12125
12126 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
12127 else t1 = tsQA ? tsQA : tsQAAAA;
12128
12129 // t2 is when the first response was received.
12130
12131 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
12132 else t2 = tsRA ? tsRA : tsRAAAA;
12133
12134 if( t1 && t2 )
12135 {
12136 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
12137 if( idleTimeUs < 0 ) idleTimeUs = 0;
12138 }
12139 else
12140 {
12141 idleTimeUs = 0;
12142 }
12143
12144 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
12145 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
12146 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
12147
12148 exit:
12149 ForgetPacketCapture( &me->pcap );
12150 if( err ) _GAITesterStop( me, err );
12151 else _GAITesterStartNextTest( me );
12152 }
12153
12154 //===========================================================================================================================
12155 // _GAITesterGetDNSMessageFromPacket
12156 //===========================================================================================================================
12157
12158 #define kHeaderSizeNullLink 4
12159 #define kHeaderSizeIPv4Min 20
12160 #define kHeaderSizeIPv6 40
12161 #define kHeaderSizeUDP 8
12162
12163 #define kIPProtocolUDP 0x11
12164
12165 static OSStatus
12166 _GAITesterGetDNSMessageFromPacket(
12167 const uint8_t * inPacketPtr,
12168 size_t inPacketLen,
12169 const uint8_t ** outMsgPtr,
12170 size_t * outMsgLen )
12171 {
12172 OSStatus err;
12173 const uint8_t * nullLink;
12174 uint32_t addressFamily;
12175 const uint8_t * ip;
12176 int ipHeaderLen;
12177 int protocol;
12178 const uint8_t * msg;
12179 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
12180
12181 nullLink = &inPacketPtr[ 0 ];
12182 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
12183 addressFamily = ReadHost32( &nullLink[ 0 ] );
12184
12185 ip = &nullLink[ kHeaderSizeNullLink ];
12186 if( addressFamily == AF_INET )
12187 {
12188 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
12189 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
12190 protocol = ip[ 9 ];
12191 }
12192 else if( addressFamily == AF_INET6 )
12193 {
12194 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
12195 ipHeaderLen = kHeaderSizeIPv6;
12196 protocol = ip[ 6 ];
12197 }
12198 else
12199 {
12200 err = kTypeErr;
12201 goto exit;
12202 }
12203 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
12204 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
12205
12206 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
12207
12208 *outMsgPtr = msg;
12209 *outMsgLen = (size_t)( end - msg );
12210 err = kNoErr;
12211
12212 exit:
12213 return( err );
12214 }
12215
12216 //===========================================================================================================================
12217 // GAITestCaseCreate
12218 //===========================================================================================================================
12219
12220 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
12221 {
12222 OSStatus err;
12223 GAITestCase * obj;
12224
12225 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
12226 require_action( obj, exit, err = kNoMemoryErr );
12227
12228 obj->title = strdup( inTitle );
12229 require_action( obj->title, exit, err = kNoMemoryErr );
12230
12231 *outCase = obj;
12232 obj = NULL;
12233 err = kNoErr;
12234
12235 exit:
12236 if( obj ) GAITestCaseFree( obj );
12237 return( err );
12238 }
12239
12240 //===========================================================================================================================
12241 // GAITestCaseFree
12242 //===========================================================================================================================
12243
12244 static void GAITestCaseFree( GAITestCase *inCase )
12245 {
12246 GAITestItem * item;
12247
12248 while( ( item = inCase->itemList ) != NULL )
12249 {
12250 inCase->itemList = item->next;
12251 GAITestItemFree( item );
12252 }
12253 ForgetMem( &inCase->title );
12254 free( inCase );
12255 }
12256
12257 //===========================================================================================================================
12258 // GAITestCaseAddItem
12259 //===========================================================================================================================
12260
12261 static OSStatus
12262 GAITestCaseAddItem(
12263 GAITestCase * inCase,
12264 unsigned int inAliasCount,
12265 unsigned int inAddressCount,
12266 int inTTL,
12267 GAITestAddrType inHasAddrs,
12268 GAITestAddrType inWantAddrs,
12269 unsigned int inTimeLimitMs,
12270 unsigned int inItemCount )
12271 {
12272 OSStatus err;
12273 GAITestItem * item;
12274 GAITestItem * item2;
12275 GAITestItem * newItemList = NULL;
12276 GAITestItem ** itemPtr;
12277 char * ptr;
12278 char * end;
12279 unsigned int i;
12280 char name[ 64 ];
12281 char tag[ kGAITesterTagStringLen + 1 ];
12282
12283 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
12284
12285 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12286
12287 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
12288 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
12289 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12290
12291 ptr = &name[ 0 ];
12292 end = &name[ countof( name ) ];
12293
12294 // Add Alias label.
12295
12296 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
12297 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
12298
12299 // Add Count label.
12300
12301 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
12302
12303 // Add TTL label.
12304
12305 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
12306
12307 // Add Tag label.
12308
12309 SNPrintF_Add( &ptr, end, "tag-%s.",
12310 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
12311
12312 // Add IPv4 or IPv6 label if necessary.
12313
12314 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
12315 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
12316
12317 // Finally, add the d.test. labels.
12318
12319 SNPrintF_Add( &ptr, end, "d.test." );
12320
12321 // Create item.
12322
12323 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
12324 require_noerr( err, exit );
12325
12326 newItemList = item;
12327 itemPtr = &item->next;
12328
12329 // Create repeat items.
12330
12331 for( i = 1; i < inItemCount; ++i )
12332 {
12333 err = GAITestItemDup( item, &item2 );
12334 require_noerr( err, exit );
12335
12336 *itemPtr = item2;
12337 itemPtr = &item2->next;
12338 }
12339
12340 // Append to test case's item list.
12341
12342 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12343 *itemPtr = newItemList;
12344 newItemList = NULL;
12345
12346 exit:
12347 while( ( item = newItemList ) != NULL )
12348 {
12349 newItemList = item->next;
12350 GAITestItemFree( item );
12351 }
12352 return( err );
12353 }
12354
12355 //===========================================================================================================================
12356 // GAITestCaseAddLocalHostItem
12357 //===========================================================================================================================
12358
12359 static OSStatus
12360 GAITestCaseAddLocalHostItem(
12361 GAITestCase * inCase,
12362 GAITestAddrType inWantAddrs,
12363 unsigned int inTimeLimitMs,
12364 unsigned int inItemCount )
12365 {
12366 OSStatus err;
12367 GAITestItem * item;
12368 GAITestItem * item2;
12369 GAITestItem * newItemList = NULL;
12370 GAITestItem ** itemPtr;
12371 unsigned int i;
12372
12373 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
12374
12375 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
12376 require_noerr( err, exit );
12377
12378 newItemList = item;
12379 itemPtr = &item->next;
12380
12381 // Create repeat items.
12382
12383 for( i = 1; i < inItemCount; ++i )
12384 {
12385 err = GAITestItemDup( item, &item2 );
12386 require_noerr( err, exit );
12387
12388 *itemPtr = item2;
12389 itemPtr = &item2->next;
12390 }
12391
12392 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12393 *itemPtr = newItemList;
12394 newItemList = NULL;
12395
12396 exit:
12397 while( ( item = newItemList ) != NULL )
12398 {
12399 newItemList = item->next;
12400 GAITestItemFree( item );
12401 }
12402 return( err );
12403 }
12404
12405 //===========================================================================================================================
12406 // GAITestItemCreate
12407 //===========================================================================================================================
12408
12409 static OSStatus
12410 GAITestItemCreate(
12411 const char * inName,
12412 unsigned int inAddressCount,
12413 GAITestAddrType inHasAddrs,
12414 GAITestAddrType inWantAddrs,
12415 unsigned int inTimeLimitMs,
12416 GAITestItem ** outItem )
12417 {
12418 OSStatus err;
12419 GAITestItem * obj = NULL;
12420
12421 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
12422 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12423 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
12424
12425 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12426 require_action( obj, exit, err = kNoMemoryErr );
12427
12428 obj->name = strdup( inName );
12429 require_action( obj->name, exit, err = kNoMemoryErr );
12430
12431 obj->addressCount = inAddressCount;
12432 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12433 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12434 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12435 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12436 obj->error = kInProgressErr;
12437 obj->timeLimitMs = inTimeLimitMs;
12438
12439 *outItem = obj;
12440 obj = NULL;
12441 err = kNoErr;
12442
12443 exit:
12444 if( obj ) GAITestItemFree( obj );
12445 return( err );
12446 }
12447
12448 //===========================================================================================================================
12449 // GAITestItemDup
12450 //===========================================================================================================================
12451
12452 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
12453 {
12454 OSStatus err;
12455 GAITestItem * obj;
12456
12457 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12458 require_action( obj, exit, err = kNoMemoryErr );
12459
12460 *obj = *inItem;
12461 obj->next = NULL;
12462 if( inItem->name )
12463 {
12464 obj->name = strdup( inItem->name );
12465 require_action( obj->name, exit, err = kNoMemoryErr );
12466 }
12467
12468 *outItem = obj;
12469 obj = NULL;
12470 err = kNoErr;
12471
12472 exit:
12473 if( obj ) GAITestItemFree( obj );
12474 return( err );
12475 }
12476
12477 //===========================================================================================================================
12478 // GAITestItemFree
12479 //===========================================================================================================================
12480
12481 static void GAITestItemFree( GAITestItem *inItem )
12482 {
12483 ForgetMem( &inItem->name );
12484 free( inItem );
12485 }
12486
12487 //===========================================================================================================================
12488 // MDNSDiscoveryTestCmd
12489 //===========================================================================================================================
12490
12491 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12492
12493 typedef struct
12494 {
12495 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12496 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
12497 NanoTime64 startTime; // When the test started.
12498 NanoTime64 endTime; // When the test ended.
12499 pid_t replierPID; // PID of mDNS replier.
12500 uint32_t ifIndex; // Index of interface to run the replier on.
12501 unsigned int instanceCount; // Desired number of service instances.
12502 unsigned int txtSize; // Desired size of each service instance's TXT record data.
12503 unsigned int recordCountA; // Desired number of A records per replier hostname.
12504 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
12505 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
12506 double ucastDropRate; // Replier's probability of dropping a unicast response.
12507 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
12508 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
12509 Boolean useIPv4; // True if the replier is to use IPv4.
12510 Boolean useIPv6; // True if the replier is to use IPv6.
12511 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
12512 char * replierCommand; // Command used to run the replier.
12513 char * serviceType; // Type of services to browse for.
12514 ServiceBrowserRef browser; // Service browser.
12515 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
12516 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12517 OutputFormatType outputFormat; // Format of test results output.
12518 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
12519 char hostname[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12520 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12521
12522 } MDNSDiscoveryTestContext;
12523
12524 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
12525 static void DNSSD_API
12526 _MDNSDiscoveryTestAboutQueryCallback(
12527 DNSServiceRef inSDRef,
12528 DNSServiceFlags inFlags,
12529 uint32_t inInterfaceIndex,
12530 DNSServiceErrorType inError,
12531 const char * inFullName,
12532 uint16_t inType,
12533 uint16_t inClass,
12534 uint16_t inRDataLen,
12535 const void * inRDataPtr,
12536 uint32_t inTTL,
12537 void * inContext );
12538 static void
12539 _MDNSDiscoveryTestServiceBrowserCallback(
12540 ServiceBrowserResults * inResults,
12541 OSStatus inError,
12542 void * inContext );
12543 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
12544
12545 static void MDNSDiscoveryTestCmd( void )
12546 {
12547 OSStatus err;
12548 MDNSDiscoveryTestContext * context;
12549 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
12550
12551 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
12552 require_action( context, exit, err = kNoMemoryErr );
12553
12554 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
12555 require_noerr_quiet( err, exit );
12556
12557 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
12558 require_noerr_quiet( err, exit );
12559
12560 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
12561 require_noerr_quiet( err, exit );
12562
12563 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
12564 require_noerr_quiet( err, exit );
12565
12566 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
12567 require_noerr_quiet( err, exit );
12568
12569 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
12570 require_noerr_quiet( err, exit );
12571
12572 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
12573 require_noerr_quiet( err, exit );
12574
12575 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
12576 require_noerr_quiet( err, exit );
12577
12578 context->replierPID = -1;
12579 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
12580 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
12581 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
12582 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
12583 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
12584 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
12585 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
12586 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
12587 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
12588 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
12589 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
12590 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
12591 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
12592
12593 if( gMDNSDiscoveryTest_Interface )
12594 {
12595 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
12596 require_noerr_quiet( err, exit );
12597 }
12598 else
12599 {
12600 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
12601 require_noerr_quiet( err, exit );
12602 }
12603
12604 err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
12605 require_noerr_quiet( err, exit );
12606
12607 if( gMDNSDiscoveryTest_FlushCache )
12608 {
12609 err = CheckRootUser();
12610 require_noerr_quiet( err, exit );
12611
12612 err = systemf( NULL, "killall -HUP mDNSResponder" );
12613 require_noerr( err, exit );
12614 sleep( 1 );
12615 context->flushedCache = true;
12616 }
12617
12618 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
12619 context->hostname );
12620 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
12621
12622 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
12623 require_action( context->serviceType, exit, err = kUnknownErr );
12624
12625 ASPrintF( &context->replierCommand,
12626 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12627 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12628 (int64_t) getpid(),
12629 context->ifIndex,
12630 context->hostname,
12631 context->tag,
12632 context->instanceCount,
12633 context->recordCountA,
12634 context->recordCountAAAA,
12635 context->ucastDropRate,
12636 context->mcastDropRate,
12637 context->maxDropCount,
12638 context->noAdditionals, " --noAdditionals",
12639 context->useIPv4, " --ipv4",
12640 context->useIPv6, " --ipv6" );
12641 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
12642
12643 err = SpawnCommand( &context->replierPID, "%s", context->replierCommand );
12644 require_noerr_quiet( err, exit );
12645
12646 // Query for the replier's about TXT record. A response means it's fully up and running.
12647
12648 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
12649 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
12650 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
12651 require_noerr( err, exit );
12652
12653 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
12654 require_noerr( err, exit );
12655
12656 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
12657 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
12658 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
12659 require_noerr( err, exit );
12660 dispatch_resume( context->queryTimer );
12661
12662 context->startTime = NanoTimeGetCurrent();
12663 dispatch_main();
12664
12665 exit:
12666 exit( 1 );
12667 }
12668
12669 //===========================================================================================================================
12670 // _MDNSDiscoveryTestFirstQueryTimeout
12671 //===========================================================================================================================
12672
12673 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
12674 {
12675 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12676
12677 dispatch_source_forget( &context->queryTimer );
12678
12679 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12680 exit( 1 );
12681 }
12682
12683 //===========================================================================================================================
12684 // _MDNSDiscoveryTestAboutQueryCallback
12685 //===========================================================================================================================
12686
12687 static void DNSSD_API
12688 _MDNSDiscoveryTestAboutQueryCallback(
12689 DNSServiceRef inSDRef,
12690 DNSServiceFlags inFlags,
12691 uint32_t inInterfaceIndex,
12692 DNSServiceErrorType inError,
12693 const char * inFullName,
12694 uint16_t inType,
12695 uint16_t inClass,
12696 uint16_t inRDataLen,
12697 const void * inRDataPtr,
12698 uint32_t inTTL,
12699 void * inContext )
12700 {
12701 OSStatus err;
12702 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12703
12704 Unused( inSDRef );
12705 Unused( inInterfaceIndex );
12706 Unused( inFullName );
12707 Unused( inType );
12708 Unused( inClass );
12709 Unused( inRDataLen );
12710 Unused( inRDataPtr );
12711 Unused( inTTL );
12712
12713 err = inError;
12714 require_noerr( err, exit );
12715 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
12716
12717 DNSServiceForget( &context->query );
12718 dispatch_source_forget( &context->queryTimer );
12719
12720 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
12721 require_noerr( err, exit );
12722
12723 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
12724 require_noerr( err, exit );
12725
12726 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
12727 ServiceBrowserStart( context->browser );
12728
12729 exit:
12730 if( err ) exit( 1 );
12731 }
12732
12733 //===========================================================================================================================
12734 // _MDNSDiscoveryTestServiceBrowserCallback
12735 //===========================================================================================================================
12736
12737 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12738 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12739 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12740 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12741 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12742 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12743 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12744 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12745 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12746 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12747 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12748
12749 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12750 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12751 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12752 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12753 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12754 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12755 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12756 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12757 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12758 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12759
12760 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12761 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12762
12763 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12764 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12765 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12766 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12767 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12768 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12769 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12770
12771 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
12772 {
12773 OSStatus err;
12774 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12775 const SBRDomain * domain;
12776 const SBRServiceType * type;
12777 const SBRServiceInstance * instance;
12778 const SBRServiceInstance ** instanceArray = NULL;
12779 const SBRIPAddress * ipaddr;
12780 size_t hostnameLen;
12781 const char * ptr;
12782 const char * end;
12783 unsigned int i;
12784 uint32_t u32;
12785 CFMutableArrayRef unexpectedInstances;
12786 CFMutableArrayRef missingInstances;
12787 CFMutableArrayRef incorrectInstances;
12788 CFMutableDictionaryRef plist = NULL;
12789 CFMutableDictionaryRef badDict = NULL;
12790 CFMutableArrayRef unexpectedAddrs = NULL;
12791 CFMutableArrayRef missingAddrs = NULL;
12792 uint64_t maxResolveTimeUs;
12793 int success = false;
12794 char startTime[ 32 ];
12795 char endTime[ 32 ];
12796
12797 context->endTime = NanoTimeGetCurrent();
12798
12799 err = inError;
12800 require_noerr( err, exit );
12801
12802 _NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
12803 _NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
12804 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
12805 "{"
12806 "%kO="
12807 "{"
12808 "%kO=%s" // replierCommand
12809 "%kO=%lli" // txtSize
12810 "%kO=%lli" // instanceCount
12811 "%kO=%lli" // recordCountA
12812 "%kO=%lli" // recordCountAAAA
12813 "%kO=%s" // hostname
12814 "%kO=%b" // noAdditionals
12815 "%kO=%f" // ucastDropRate
12816 "%kO=%f" // mcastDropRate
12817 "%kO=%i" // maxDropCount
12818 "}"
12819 "%kO=%s" // startTime
12820 "%kO=%s" // endTime
12821 "%kO=%lli" // browseTimeSecs
12822 "%kO=%s" // serviceType
12823 "%kO=%b" // flushedCache
12824 "%kO=[%@]" // unexpectedInstances
12825 "%kO=[%@]" // missingInstances
12826 "%kO=[%@]" // incorrectInstances
12827 "}",
12828 kMDNSDiscoveryTestResultsKey_ReplierInfo,
12829 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
12830 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
12831 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
12832 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
12833 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
12834 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
12835 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
12836 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
12837 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
12838 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
12839 kMDNSDiscoveryTestResultsKey_StartTime, startTime,
12840 kMDNSDiscoveryTestResultsKey_EndTime, endTime,
12841 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
12842 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
12843 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
12844 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
12845 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
12846 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
12847 require_noerr( err, exit );
12848
12849 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
12850 require_action( domain, exit, err = kInternalErr );
12851
12852 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
12853 require_action( type, exit, err = kInternalErr );
12854
12855 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
12856 require_action( instanceArray, exit, err = kNoMemoryErr );
12857
12858 hostnameLen = strlen( context->hostname );
12859 for( instance = type->instanceList; instance; instance = instance->next )
12860 {
12861 unsigned int instanceNumber = 0;
12862
12863 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
12864 {
12865 ptr = &instance->name[ hostnameLen ];
12866 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
12867 {
12868 ptr += 2;
12869 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12870 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12871 {
12872 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
12873 {
12874 instanceNumber = u32;
12875 }
12876 }
12877 }
12878 else if( *ptr == '\0' )
12879 {
12880 instanceNumber = 1;
12881 }
12882 }
12883 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
12884 {
12885 check( !instanceArray[ instanceNumber - 1 ] );
12886 instanceArray[ instanceNumber - 1 ] = instance;
12887 }
12888 else
12889 {
12890 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
12891 "{"
12892 "%kO=%s"
12893 "%kO=%lli"
12894 "}",
12895 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
12896 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
12897 require_noerr( err, exit );
12898 }
12899 }
12900
12901 maxResolveTimeUs = 0;
12902 for( i = 1; i <= context->instanceCount; ++i )
12903 {
12904 int isHostnameValid;
12905 int isTXTValid;
12906
12907 instance = instanceArray[ i - 1 ];
12908 if( !instance )
12909 {
12910 if( i == 1 )
12911 {
12912 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
12913 require_noerr( err, exit );
12914 }
12915 else
12916 {
12917 char * instanceName = NULL;
12918
12919 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
12920 require_action( instanceName, exit, err = kUnknownErr );
12921
12922 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
12923 free( instanceName );
12924 require_noerr( err, exit );
12925 }
12926 continue;
12927 }
12928
12929 if( !instance->hostname )
12930 {
12931 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
12932 "{"
12933 "%kO=%s"
12934 "%kO=%b"
12935 "}",
12936 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12937 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
12938 require_noerr( err, exit );
12939 continue;
12940 }
12941
12942 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
12943 require_action( badDict, exit, err = kNoMemoryErr );
12944
12945 isHostnameValid = false;
12946 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
12947 {
12948 ptr = &instance->hostname[ hostnameLen ];
12949 if( i == 1 )
12950 {
12951 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
12952 }
12953 else if( *ptr == '-' )
12954 {
12955 ++ptr;
12956 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12957 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12958 {
12959 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
12960 }
12961 }
12962 }
12963 if( !isHostnameValid )
12964 {
12965 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
12966 kSizeCString );
12967 require_noerr( err, exit );
12968 }
12969
12970 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
12971 {
12972 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
12973 require_noerr( err, exit );
12974 }
12975
12976 isTXTValid = false;
12977 if( instance->txtLen == context->txtSize )
12978 {
12979 uint8_t name[ kDomainNameLengthMax ];
12980
12981 err = DomainNameFromString( name, instance->name, NULL );
12982 require_noerr( err, exit );
12983
12984 err = DomainNameAppendString( name, type->name, NULL );
12985 require_noerr( err, exit );
12986
12987 err = DomainNameAppendString( name, "local", NULL );
12988 require_noerr( err, exit );
12989
12990 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
12991 }
12992 if( !isTXTValid )
12993 {
12994 char * hexStr = NULL;
12995
12996 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
12997 require_action( hexStr, exit, err = kUnknownErr );
12998
12999 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
13000 free( hexStr );
13001 require_noerr( err, exit );
13002 }
13003
13004 if( isHostnameValid )
13005 {
13006 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
13007 unsigned int j;
13008 uint8_t addrV4[ 4 ];
13009 uint8_t addrV6[ 16 ];
13010
13011 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
13012 else addrV4Bitmap = ~UINT64_C( 0 );
13013
13014 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
13015 else addrV6Bitmap = ~UINT64_C( 0 );
13016
13017 addrV4[ 0 ] = 0;
13018 WriteBig16( &addrV4[ 1 ], i );
13019 addrV4[ 3 ] = 0;
13020
13021 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
13022 WriteBig16( &addrV6[ 12 ], i );
13023
13024 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13025 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
13026
13027 resolveTimeUs = 0;
13028 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
13029 {
13030 const uint8_t * addrPtr;
13031 unsigned int lsb;
13032 int isAddrValid = false;
13033
13034 if( ipaddr->sip.sa.sa_family == AF_INET )
13035 {
13036 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
13037 lsb = addrPtr[ 3 ];
13038 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
13039 {
13040 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
13041 addrV4Bitmap &= ~bitmask;
13042 isAddrValid = true;
13043 }
13044 }
13045 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
13046 {
13047 addrPtr = ipaddr->sip.v6.sin6_addr.s6_addr;
13048 lsb = addrPtr[ 15 ];
13049 if( ( memcmp( addrPtr, addrV6, 15 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
13050 {
13051 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
13052 addrV6Bitmap &= ~bitmask;
13053 isAddrValid = true;
13054 }
13055 }
13056 if( isAddrValid )
13057 {
13058 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
13059 }
13060 else
13061 {
13062 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
13063 require_noerr( err, exit );
13064 }
13065 }
13066
13067 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
13068 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
13069
13070 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
13071 {
13072 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
13073 }
13074 ForgetCF( &unexpectedAddrs );
13075
13076 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13077 require_action( missingAddrs, exit, err = kNoMemoryErr );
13078
13079 for( j = 1; addrV4Bitmap != 0; ++j )
13080 {
13081 bitmask = UINT64_C( 1 ) << ( j - 1 );
13082 if( addrV4Bitmap & bitmask )
13083 {
13084 addrV4Bitmap &= ~bitmask;
13085 addrV4[ 3 ] = (uint8_t) j;
13086 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
13087 require_noerr( err, exit );
13088 }
13089 }
13090 for( j = 1; addrV6Bitmap != 0; ++j )
13091 {
13092 bitmask = UINT64_C( 1 ) << ( j - 1 );
13093 if( addrV6Bitmap & bitmask )
13094 {
13095 addrV6Bitmap &= ~bitmask;
13096 addrV6[ 15 ] = (uint8_t) j;
13097 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.16a", addrV6 );
13098 require_noerr( err, exit );
13099 }
13100 }
13101
13102 if( CFArrayGetCount( missingAddrs ) > 0 )
13103 {
13104 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
13105 }
13106 ForgetCF( &missingAddrs );
13107 }
13108
13109 if( CFDictionaryGetCount( badDict ) > 0 )
13110 {
13111 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
13112 kSizeCString );
13113 require_noerr( err, exit );
13114
13115 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
13116 CFArrayAppendValue( incorrectInstances, badDict );
13117 }
13118 ForgetCF( &badDict );
13119 }
13120
13121 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
13122 ( CFArrayGetCount( missingInstances ) == 0 ) &&
13123 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
13124 {
13125 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
13126 require_noerr( err, exit );
13127 success = true;
13128 }
13129 else
13130 {
13131 success = false;
13132 }
13133 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
13134
13135 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
13136 require_noerr_quiet( err, exit );
13137
13138 exit:
13139 ForgetCF( &context->browser );
13140 if( context->replierPID != -1 )
13141 {
13142 kill( context->replierPID, SIGTERM );
13143 context->replierPID = -1;
13144 }
13145 FreeNullSafe( instanceArray );
13146 CFReleaseNullSafe( plist );
13147 CFReleaseNullSafe( badDict );
13148 CFReleaseNullSafe( unexpectedAddrs );
13149 CFReleaseNullSafe( missingAddrs );
13150 exit( err ? 1 : ( success ? 0 : 2 ) );
13151 }
13152
13153 //===========================================================================================================================
13154 // _MDNSDiscoveryTestTXTRecordIsValid
13155 //===========================================================================================================================
13156
13157 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
13158 {
13159 uint32_t hash;
13160 int n;
13161 const uint8_t * ptr;
13162 size_t i, wholeCount, remCount;
13163 uint8_t txtStr[ 16 ];
13164
13165 if( inTXTLen == 0 ) return( false );
13166
13167 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
13168
13169 txtStr[ 0 ] = 15;
13170 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
13171 check( n == 15 );
13172
13173 ptr = inTXTPtr;
13174 wholeCount = inTXTLen / 16;
13175 for( i = 0; i < wholeCount; ++i )
13176 {
13177 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
13178 ptr += 16;
13179 }
13180
13181 remCount = inTXTLen % 16;
13182 if( remCount > 0 )
13183 {
13184 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
13185 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
13186 ptr += remCount;
13187 }
13188 check( ptr == &inTXTPtr[ inTXTLen ] );
13189 return( true );
13190 }
13191
13192 //===========================================================================================================================
13193 // DotLocalTestCmd
13194 //===========================================================================================================================
13195
13196 #define kDotLocalTestPreparationTimeLimitSecs 5
13197 #define kDotLocalTestSubtestDurationSecs 5
13198
13199 // Constants for SRV record query subtest.
13200
13201 #define kDotLocalTestSRV_Priority 1
13202 #define kDotLocalTestSRV_Weight 0
13203 #define kDotLocalTestSRV_Port 80
13204 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
13205 #define kDotLocalTestSRV_TargetStr "www.example.com."
13206 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
13207
13208 typedef enum
13209 {
13210 kDotLocalTestState_Unset = 0,
13211 kDotLocalTestState_Preparing = 1,
13212 kDotLocalTestState_GAIMDNSOnly = 2,
13213 kDotLocalTestState_GAIDNSOnly = 3,
13214 kDotLocalTestState_GAIBoth = 4,
13215 kDotLocalTestState_GAINeither = 5,
13216 kDotLocalTestState_GAINoSuchRecord = 6,
13217 kDotLocalTestState_QuerySRV = 7,
13218 kDotLocalTestState_Done = 8
13219
13220 } DotLocalTestState;
13221
13222 typedef struct
13223 {
13224 const char * testDesc; // Description of the current subtest.
13225 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
13226 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
13227 NanoTime64 startTime; // Timestamp of when the subtest started.
13228 NanoTime64 endTime; // Timestamp of when the subtest ended.
13229 CFMutableArrayRef correctResults; // Operation results that were expected.
13230 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
13231 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
13232 OSStatus error; // Subtest's error code.
13233 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
13234 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
13235 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
13236 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
13237 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
13238 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
13239 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
13240 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
13241 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13242 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13243 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13244 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13245 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
13246
13247 } DotLocalSubtest;
13248
13249 typedef struct
13250 {
13251 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
13252 DotLocalSubtest * subtest; // Current subtest's state.
13253 DNSServiceRef connection; // Shared connection for DNS-SD operations.
13254 DNSServiceRef op; // Reference for the current DNS-SD operation.
13255 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
13256 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13257 char * replierCmd; // Command used to invoke the mdnsreplier.
13258 char * serverCmd; // Command used to invoke the test DNS server.
13259 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
13260 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13261 NanoTime64 startTime; // Timestamp for when the test started.
13262 NanoTime64 endTime; // Timestamp for when the test ended.
13263 DotLocalTestState state; // The test's current state.
13264 pid_t replierPID; // PID of spawned mdnsreplier.
13265 pid_t serverPID; // PID of spawned test DNS server.
13266 uint32_t ifIndex; // Interface index used for mdnsreplier.
13267 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
13268 OutputFormatType outputFormat; // Format of test results output.
13269 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
13270 Boolean serverIsReady; // True if response was received for test DNS server probe query.
13271 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
13272 Boolean testFailed; // True if at least one subtest failed.
13273 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13274 // The format of this string is "dotlocal-test-<six random chars>".
13275 } DotLocalTestContext;
13276
13277 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
13278 static void DNSSD_API
13279 _DotLocalTestProbeQueryRecordCallback(
13280 DNSServiceRef inSDRef,
13281 DNSServiceFlags inFlags,
13282 uint32_t inInterfaceIndex,
13283 DNSServiceErrorType inError,
13284 const char * inFullName,
13285 uint16_t inType,
13286 uint16_t inClass,
13287 uint16_t inRDataLen,
13288 const void * inRDataPtr,
13289 uint32_t inTTL,
13290 void * inContext );
13291 static void DNSSD_API
13292 _DotLocalTestRegisterRecordCallback(
13293 DNSServiceRef inSDRef,
13294 DNSRecordRef inRecordRef,
13295 DNSServiceFlags inFlags,
13296 DNSServiceErrorType inError,
13297 void * inContext );
13298 static void _DotLocalTestTimerHandler( void *inContext );
13299 static void DNSSD_API
13300 _DotLocalTestGAICallback(
13301 DNSServiceRef inSDRef,
13302 DNSServiceFlags inFlags,
13303 uint32_t inInterfaceIndex,
13304 DNSServiceErrorType inError,
13305 const char * inHostname,
13306 const struct sockaddr * inSockAddr,
13307 uint32_t inTTL,
13308 void * inContext );
13309 static void DNSSD_API
13310 _DotLocalTestQueryRecordCallback(
13311 DNSServiceRef inSDRef,
13312 DNSServiceFlags inFlags,
13313 uint32_t inInterfaceIndex,
13314 DNSServiceErrorType inError,
13315 const char * inFullName,
13316 uint16_t inType,
13317 uint16_t inClass,
13318 uint16_t inRDataLen,
13319 const void * inRDataPtr,
13320 uint32_t inTTL,
13321 void * inContext );
13322
13323 static void DotLocalTestCmd( void )
13324 {
13325 OSStatus err;
13326 DotLocalTestContext * context;
13327 uint8_t * rdataPtr;
13328 size_t rdataLen;
13329 DNSServiceFlags flags;
13330 char queryName[ 64 ];
13331 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
13332
13333 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
13334 require_action( context, exit, err = kNoMemoryErr );
13335
13336 context->startTime = NanoTimeGetCurrent();
13337 context->endTime = kNanoTime_Invalid;
13338
13339 context->state = kDotLocalTestState_Preparing;
13340
13341 if( gDotLocalTest_Interface )
13342 {
13343 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
13344 require_noerr_quiet( err, exit );
13345 }
13346 else
13347 {
13348 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
13349 require_noerr_quiet( err, exit );
13350 }
13351
13352 if( gDotLocalTest_OutputFilePath )
13353 {
13354 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
13355 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
13356 }
13357
13358 err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
13359 require_noerr_quiet( err, exit );
13360
13361 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13362 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
13363
13364 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13365 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
13366
13367 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
13368 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
13369
13370 // Spawn an mdnsreplier.
13371
13372 ASPrintF( &context->replierCmd,
13373 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13374 " --countAAAA 1",
13375 (int64_t) getpid(), context->ifIndex, context->labelStr,
13376 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
13377 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
13378
13379 err = SpawnCommand( &context->replierPID, "%s", context->replierCmd );
13380 require_noerr( err, exit );
13381
13382 // Spawn a test DNS server
13383
13384 ASPrintF( &context->serverCmd,
13385 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13386 (int64_t) getpid(), context->labelStr );
13387 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
13388
13389 err = SpawnCommand( &context->serverPID, "%s", context->serverCmd );
13390 require_noerr( err, exit );
13391
13392 // Create a shared DNS-SD connection.
13393
13394 err = DNSServiceCreateConnection( &context->connection );
13395 require_noerr( err, exit );
13396
13397 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
13398 require_noerr( err, exit );
13399
13400 // Create probe query for DNS server, i.e., query for any name that has an A record.
13401
13402 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
13403
13404 flags = kDNSServiceFlagsShareConnection;
13405 #if( TARGET_OS_WATCH )
13406 flags |= kDNSServiceFlagsPathEvaluationDone;
13407 #endif
13408
13409 context->op = context->connection;
13410 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
13411 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
13412 require_noerr( err, exit );
13413
13414 // Create probe query for mdnsreplier's "about" TXT record.
13415
13416 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
13417
13418 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
13419 #if( TARGET_OS_WATCH )
13420 flags |= kDNSServiceFlagsPathEvaluationDone;
13421 #endif
13422
13423 context->op2 = context->connection;
13424 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
13425 _DotLocalTestProbeQueryRecordCallback, context );
13426 require_noerr( err, exit );
13427
13428 // Register a dummy local. SOA record.
13429
13430 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
13431 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
13432 require_noerr( err, exit );
13433
13434 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
13435 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN, 1,
13436 rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
13437 require_noerr( err, exit );
13438
13439 // Start timer for probe responses and SOA record registration.
13440
13441 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
13442 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13443 _DotLocalTestTimerHandler, context, &context->timer );
13444 require_noerr( err, exit );
13445 dispatch_resume( context->timer );
13446
13447 dispatch_main();
13448
13449 exit:
13450 if( err ) ErrQuit( 1, "error: %#m\n", err );
13451 }
13452
13453 //===========================================================================================================================
13454 // _DotLocalTestStateMachine
13455 //===========================================================================================================================
13456
13457 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
13458 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
13459 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
13460 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
13461 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
13462
13463 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
13464 {
13465 OSStatus err;
13466 DotLocalTestState nextState;
13467
13468 DNSServiceForget( &inContext->op );
13469 DNSServiceForget( &inContext->op2 );
13470 dispatch_source_forget( &inContext->timer );
13471
13472 switch( inContext->state )
13473 {
13474 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
13475 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
13476 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
13477 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
13478 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
13479 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
13480 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
13481 default: err = kStateErr; goto exit;
13482 }
13483
13484 if( inContext->state == kDotLocalTestState_Preparing )
13485 {
13486 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
13487 {
13488 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13489 YesNoStr( inContext->registeredSOA ),
13490 YesNoStr( inContext->serverIsReady ),
13491 YesNoStr( inContext->replierIsReady ) );
13492 err = kNotPreparedErr;
13493 goto exit;
13494 }
13495 }
13496 else
13497 {
13498 err = _DotLocalTestFinalizeSubtest( inContext );
13499 require_noerr( err, exit );
13500 }
13501
13502 inContext->state = nextState;
13503 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
13504 err = _DotLocalTestStartSubtest( inContext );
13505
13506 exit:
13507 if( err ) ErrQuit( 1, "error: %#m\n", err );
13508 }
13509
13510 //===========================================================================================================================
13511 // _DotLocalSubtestCreate
13512 //===========================================================================================================================
13513
13514 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
13515 {
13516 OSStatus err;
13517 DotLocalSubtest * obj;
13518
13519 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
13520 require_action( obj, exit, err = kNoMemoryErr );
13521
13522 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13523 require_action( obj->correctResults, exit, err = kNoMemoryErr );
13524
13525 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13526 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
13527
13528 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13529 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
13530
13531 *outSubtest = obj;
13532 obj = NULL;
13533 err = kNoErr;
13534
13535 exit:
13536 if( obj ) _DotLocalSubtestFree( obj );
13537 return( err );
13538 }
13539
13540 //===========================================================================================================================
13541 // _DotLocalSubtestFree
13542 //===========================================================================================================================
13543
13544 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
13545 {
13546 ForgetMem( &inSubtest->queryName );
13547 ForgetCF( &inSubtest->correctResults );
13548 ForgetCF( &inSubtest->duplicateResults );
13549 ForgetCF( &inSubtest->unexpectedResults );
13550 free( inSubtest );
13551 }
13552
13553 //===========================================================================================================================
13554 // _DotLocalTestStartSubtest
13555 //===========================================================================================================================
13556
13557 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
13558 {
13559 OSStatus err;
13560 DotLocalSubtest * subtest = NULL;
13561 DNSServiceRef op = NULL;
13562 DNSServiceFlags flags;
13563
13564 err = _DotLocalSubtestCreate( &subtest );
13565 require_noerr( err, exit );
13566
13567 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
13568 {
13569 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
13570 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13571
13572 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13573 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13574
13575 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
13576 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::2:1
13577 subtest->addrMDNSv6[ 13 ] = 2;
13578 subtest->addrMDNSv6[ 15 ] = 1;
13579
13580 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
13581 }
13582
13583 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
13584 {
13585 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
13586 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13587
13588 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13589 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13590
13591 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13592 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13593 subtest->addrDNSv6[ 15 ] = 1;
13594
13595 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
13596 }
13597
13598 else if( inContext->state == kDotLocalTestState_GAIBoth )
13599 {
13600 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
13601 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13602
13603 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13604 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13605 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13606 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13607
13608 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13609 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13610 subtest->addrDNSv6[ 15 ] = 1;
13611
13612 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
13613 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::1:1
13614 subtest->addrMDNSv6[ 13 ] = 1;
13615 subtest->addrMDNSv6[ 15 ] = 1;
13616
13617 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
13618 }
13619
13620 else if( inContext->state == kDotLocalTestState_GAINeither )
13621 {
13622 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
13623 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13624
13625 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
13626 }
13627
13628 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
13629 {
13630 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
13631 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13632
13633 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13634 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13635 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
13636 }
13637
13638 else if( inContext->state == kDotLocalTestState_QuerySRV )
13639 {
13640 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13641 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
13642 inContext->labelStr );
13643 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13644
13645 subtest->needSRV = true;
13646 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
13647 }
13648
13649 else
13650 {
13651 err = kStateErr;
13652 goto exit;
13653 }
13654
13655 // Start new operation.
13656
13657 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
13658 #if( TARGET_OS_WATCH )
13659 flags |= kDNSServiceFlagsPathEvaluationDone;
13660 #endif
13661
13662 subtest->startTime = NanoTimeGetCurrent();
13663 subtest->endTime = kNanoTime_Invalid;
13664
13665 if( inContext->state == kDotLocalTestState_QuerySRV )
13666 {
13667 op = inContext->connection;
13668 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
13669 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
13670 require_noerr( err, exit );
13671 }
13672 else
13673 {
13674 op = inContext->connection;
13675 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
13676 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
13677 require_noerr( err, exit );
13678 }
13679
13680 // Start timer.
13681
13682 check( !inContext->timer );
13683 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
13684 INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13685 _DotLocalTestTimerHandler, inContext, &inContext->timer );
13686 require_noerr( err, exit );
13687 dispatch_resume( inContext->timer );
13688
13689 check( !inContext->op );
13690 inContext->op = op;
13691 op = NULL;
13692
13693 check( !inContext->subtest );
13694 inContext->subtest = subtest;
13695 subtest = NULL;
13696
13697 exit:
13698 if( subtest ) _DotLocalSubtestFree( subtest );
13699 if( op ) DNSServiceRefDeallocate( op );
13700 return( err );
13701 }
13702
13703 //===========================================================================================================================
13704 // _DotLocalTestFinalizeSubtest
13705 //===========================================================================================================================
13706
13707 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13708 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13709 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13710 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13711 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13712 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13713 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13714 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13715 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13716 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13717 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13718 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13719 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13720 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13721 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13722
13723 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
13724 {
13725 OSStatus err;
13726 DotLocalSubtest * subtest;
13727 CFMutableDictionaryRef reportDict;
13728 CFMutableDictionaryRef resultsDict;
13729 CFMutableArrayRef missingResults, reportArray;
13730 char startTime[ 32 ];
13731 char endTime[ 32 ];
13732
13733 subtest = inContext->subtest;
13734 inContext->subtest = NULL;
13735
13736 subtest->endTime = NanoTimeGetCurrent();
13737 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
13738 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
13739
13740 reportDict = NULL;
13741 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
13742 "{"
13743 "%kO=%s" // startTime
13744 "%kO=%s" // endTime
13745 "%kO=%s" // queryName
13746 "%kO=%s" // description
13747 "%kO={%@}" // results
13748 "}",
13749 kDotLocalTestReportKey_StartTime, startTime,
13750 kDotLocalTestReportKey_EndTime, endTime,
13751 kDotLocalTestReportKey_QueryName, subtest->queryName,
13752 kDotLocalTestReportKey_Description, subtest->testDesc,
13753 kDotLocalTestReportKey_Results, &resultsDict );
13754 require_noerr( err, exit );
13755
13756 missingResults = NULL;
13757 switch( inContext->state )
13758 {
13759 case kDotLocalTestState_GAIMDNSOnly:
13760 case kDotLocalTestState_GAIDNSOnly:
13761 case kDotLocalTestState_GAIBoth:
13762 case kDotLocalTestState_GAINeither:
13763 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
13764 {
13765 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13766 "["
13767 "%.4a" // Expected DNS IPv4 address
13768 "%.16a" // Expected DNS IPv6 address
13769 "%.4a" // Expected MDNS IPv4 address
13770 "%.16a" // Expected MDNS IPv6 address
13771 "]",
13772 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
13773 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
13774 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
13775 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
13776 require_noerr( err, exit );
13777 }
13778 break;
13779
13780 case kDotLocalTestState_QuerySRV:
13781 if( subtest->needSRV )
13782 {
13783 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13784 "["
13785 "%s" // Expected SRV record data as a string.
13786 "]",
13787 kDotLocalTestSRV_ResultStr );
13788 require_noerr( err, exit );
13789 }
13790 break;
13791
13792 case kDotLocalTestState_GAINoSuchRecord:
13793 if( subtest->needDNSv4 || subtest->needDNSv6 )
13794 {
13795 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13796 "["
13797 "%s" // No Such Record (A)
13798 "%s" // No Such Record (AAAA)
13799 "]",
13800 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
13801 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
13802 require_noerr( err, exit );
13803 }
13804 break;
13805
13806 default:
13807 err = kStateErr;
13808 goto exit;
13809 }
13810
13811 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
13812
13813 if( missingResults )
13814 {
13815 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
13816 ForgetCF( &missingResults );
13817 if( !subtest->error ) subtest->error = kNotFoundErr;
13818 }
13819
13820 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
13821 {
13822 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
13823 if( !subtest->error ) subtest->error = kUnexpectedErr;
13824 }
13825
13826 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
13827 {
13828 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
13829 if( !subtest->error ) subtest->error = kDuplicateErr;
13830 }
13831
13832 if( subtest->error ) inContext->testFailed = true;
13833 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
13834 require_noerr( err, exit );
13835
13836 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
13837 CFArrayAppendValue( reportArray, reportDict );
13838
13839 exit:
13840 _DotLocalSubtestFree( subtest );
13841 CFReleaseNullSafe( reportDict );
13842 return( err );
13843 }
13844
13845 //===========================================================================================================================
13846 // _DotLocalTestFinalizeAndExit
13847 //===========================================================================================================================
13848
13849 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
13850 {
13851 OSStatus err;
13852 CFPropertyListRef plist;
13853 char timestampStart[ 32 ];
13854 char timestampEnd[ 32 ];
13855
13856 check( !inContext->subtest );
13857 inContext->endTime = NanoTimeGetCurrent();
13858
13859 if( inContext->replierPID != -1 )
13860 {
13861 kill( inContext->replierPID, SIGTERM );
13862 inContext->replierPID = -1;
13863 }
13864 if( inContext->serverPID != -1 )
13865 {
13866 kill( inContext->serverPID, SIGTERM );
13867 inContext->serverPID = -1;
13868 }
13869 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
13870 require_noerr( err, exit );
13871
13872 _NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
13873 _NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
13874
13875 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13876 "{"
13877 "%kO=%s" // startTime
13878 "%kO=%s" // endTime
13879 "%kO=%O" // testsGAI
13880 "%kO=%O" // testsQuerySRV
13881 "%kO=%b" // success
13882 "%kO=%s" // replierCmd
13883 "%kO=%s" // serverCmd
13884 "}",
13885 kDotLocalTestReportKey_StartTime, timestampStart,
13886 kDotLocalTestReportKey_EndTime, timestampEnd,
13887 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
13888 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
13889 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
13890 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
13891 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
13892 require_noerr( err, exit );
13893
13894 ForgetCF( &inContext->reportsGAI );
13895 ForgetCF( &inContext->reportsQuerySRV );
13896
13897 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
13898 CFRelease( plist );
13899 require_noerr( err, exit );
13900
13901 exit( inContext->testFailed ? 2 : 0 );
13902
13903 exit:
13904 ErrQuit( 1, "error: %#m\n", err );
13905 }
13906
13907 //===========================================================================================================================
13908 // _DotLocalTestProbeQueryRecordCallback
13909 //===========================================================================================================================
13910
13911 static void DNSSD_API
13912 _DotLocalTestProbeQueryRecordCallback(
13913 DNSServiceRef inSDRef,
13914 DNSServiceFlags inFlags,
13915 uint32_t inInterfaceIndex,
13916 DNSServiceErrorType inError,
13917 const char * inFullName,
13918 uint16_t inType,
13919 uint16_t inClass,
13920 uint16_t inRDataLen,
13921 const void * inRDataPtr,
13922 uint32_t inTTL,
13923 void * inContext )
13924 {
13925 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13926
13927 Unused( inInterfaceIndex );
13928 Unused( inFullName );
13929 Unused( inType );
13930 Unused( inClass );
13931 Unused( inRDataLen );
13932 Unused( inRDataPtr );
13933 Unused( inTTL );
13934
13935 check( context->state == kDotLocalTestState_Preparing );
13936
13937 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
13938
13939 if( inSDRef == context->op )
13940 {
13941 DNSServiceForget( &context->op );
13942 context->serverIsReady = true;
13943 }
13944 else if( inSDRef == context->op2 )
13945 {
13946 DNSServiceForget( &context->op2 );
13947 context->replierIsReady = true;
13948 }
13949
13950 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
13951 {
13952 _DotLocalTestStateMachine( context );
13953 }
13954
13955 exit:
13956 return;
13957 }
13958
13959 //===========================================================================================================================
13960 // _DotLocalTestRegisterRecordCallback
13961 //===========================================================================================================================
13962
13963 static void DNSSD_API
13964 _DotLocalTestRegisterRecordCallback(
13965 DNSServiceRef inSDRef,
13966 DNSRecordRef inRecordRef,
13967 DNSServiceFlags inFlags,
13968 DNSServiceErrorType inError,
13969 void * inContext )
13970 {
13971 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13972
13973 Unused( inSDRef );
13974 Unused( inRecordRef );
13975 Unused( inFlags );
13976
13977 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
13978
13979 if( !context->registeredSOA )
13980 {
13981 context->registeredSOA = true;
13982 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
13983 }
13984 }
13985
13986 //===========================================================================================================================
13987 // _DotLocalTestTimerHandler
13988 //===========================================================================================================================
13989
13990 static void _DotLocalTestTimerHandler( void *inContext )
13991 {
13992 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
13993 }
13994
13995 //===========================================================================================================================
13996 // _DotLocalTestGAICallback
13997 //===========================================================================================================================
13998
13999 static void DNSSD_API
14000 _DotLocalTestGAICallback(
14001 DNSServiceRef inSDRef,
14002 DNSServiceFlags inFlags,
14003 uint32_t inInterfaceIndex,
14004 DNSServiceErrorType inError,
14005 const char * inHostname,
14006 const struct sockaddr * inSockAddr,
14007 uint32_t inTTL,
14008 void * inContext )
14009 {
14010 OSStatus err;
14011 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
14012 DotLocalSubtest * const subtest = context->subtest;
14013 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
14014
14015 Unused( inSDRef );
14016 Unused( inInterfaceIndex );
14017 Unused( inHostname );
14018 Unused( inTTL );
14019
14020 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
14021 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
14022
14023 if( context->state == kDotLocalTestState_GAINoSuchRecord )
14024 {
14025 if( inError == kDNSServiceErr_NoSuchRecord )
14026 {
14027 CFMutableArrayRef array = NULL;
14028 const char * noSuchRecordStr;
14029
14030 if( sip->sa.sa_family == AF_INET )
14031 {
14032 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
14033 subtest->needDNSv4 = false;
14034
14035 noSuchRecordStr = kNoSuchRecordAStr;
14036 }
14037 else
14038 {
14039 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
14040 subtest->needDNSv6 = false;
14041
14042 noSuchRecordStr = kNoSuchRecordAAAAStr;
14043 }
14044 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
14045 require_noerr( err, fatal );
14046 }
14047 else if( !inError )
14048 {
14049 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
14050 require_noerr( err, fatal );
14051 }
14052 else
14053 {
14054 err = inError;
14055 goto exit;
14056 }
14057 }
14058 else
14059 {
14060 if( !inError )
14061 {
14062 CFMutableArrayRef array = NULL;
14063
14064 if( sip->sa.sa_family == AF_INET )
14065 {
14066 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
14067
14068 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
14069 {
14070 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
14071 subtest->needDNSv4 = false;
14072 }
14073 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
14074 {
14075 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
14076 subtest->needMDNSv4 = false;
14077 }
14078 }
14079 else
14080 {
14081 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
14082
14083 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
14084 {
14085 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
14086 subtest->needDNSv6 = false;
14087 }
14088 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
14089 {
14090 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
14091 subtest->needMDNSv6 = false;
14092 }
14093 }
14094 if( !array ) array = subtest->unexpectedResults;
14095 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
14096 require_noerr( err, fatal );
14097 }
14098 else if( inError == kDNSServiceErr_NoSuchRecord )
14099 {
14100 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
14101 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
14102 require_noerr( err, fatal );
14103 }
14104 else
14105 {
14106 err = inError;
14107 goto exit;
14108 }
14109 }
14110
14111 exit:
14112 if( err )
14113 {
14114 subtest->error = err;
14115 _DotLocalTestStateMachine( context );
14116 }
14117 return;
14118
14119 fatal:
14120 ErrQuit( 1, "error: %#m\n", err );
14121 }
14122
14123 //===========================================================================================================================
14124 // _DotLocalTestQueryRecordCallback
14125 //===========================================================================================================================
14126
14127 static void DNSSD_API
14128 _DotLocalTestQueryRecordCallback(
14129 DNSServiceRef inSDRef,
14130 DNSServiceFlags inFlags,
14131 uint32_t inInterfaceIndex,
14132 DNSServiceErrorType inError,
14133 const char * inFullName,
14134 uint16_t inType,
14135 uint16_t inClass,
14136 uint16_t inRDataLen,
14137 const void * inRDataPtr,
14138 uint32_t inTTL,
14139 void * inContext )
14140 {
14141 OSStatus err;
14142 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
14143 DotLocalSubtest * const subtest = context->subtest;
14144 const dns_fixed_fields_srv * fields;
14145 const uint8_t * target;
14146 const uint8_t * ptr;
14147 const uint8_t * end;
14148 char * rdataStr;
14149 unsigned int priority, weight, port;
14150 CFMutableArrayRef array;
14151
14152 Unused( inSDRef );
14153 Unused( inInterfaceIndex );
14154 Unused( inFullName );
14155 Unused( inTTL );
14156
14157 check( context->state == kDotLocalTestState_QuerySRV );
14158
14159 err = inError;
14160 require_noerr_quiet( err, exit );
14161 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
14162 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
14163 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
14164
14165 fields = (const dns_fixed_fields_srv *) inRDataPtr;
14166 priority = dns_fixed_fields_srv_get_priority( fields );
14167 weight = dns_fixed_fields_srv_get_weight( fields );
14168 port = dns_fixed_fields_srv_get_port( fields );
14169 target = (const uint8_t *) &fields[ 1 ];
14170 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
14171 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
14172
14173 if( ( priority == kDotLocalTestSRV_Priority ) &&
14174 ( weight == kDotLocalTestSRV_Weight ) &&
14175 ( port == kDotLocalTestSRV_Port ) &&
14176 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
14177 {
14178 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
14179 subtest->needSRV = false;
14180 }
14181 else
14182 {
14183 array = subtest->unexpectedResults;
14184 }
14185
14186 rdataStr = NULL;
14187 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, NULL, 0, &rdataStr );
14188 if( !rdataStr )
14189 {
14190 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
14191 require_action( rdataStr, fatal, err = kNoMemoryErr );
14192 }
14193
14194 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
14195 free( rdataStr );
14196 require_noerr( err, fatal );
14197
14198 exit:
14199 if( err )
14200 {
14201 subtest->error = err;
14202 _DotLocalTestStateMachine( context );
14203 }
14204 return;
14205
14206 fatal:
14207 ErrQuit( 1, "error: %#m\n", err );
14208 }
14209
14210 //===========================================================================================================================
14211 // ProbeConflictTestCmd
14212 //===========================================================================================================================
14213
14214 #define kProbeConflictTestService_DefaultName "pctest-name"
14215 #define kProbeConflictTestService_Port 60000
14216
14217 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
14218 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
14219
14220 typedef struct
14221 {
14222 const char * description;
14223 const char * program;
14224 Boolean expectsRename;
14225
14226 } ProbeConflictTestCase;
14227
14228 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
14229 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
14230 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
14231
14232 static const ProbeConflictTestCase kProbeConflictTestCases[] =
14233 {
14234 // No conflicts
14235
14236 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
14237
14238 // One multicast probe conflict
14239
14240 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
14241 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
14242 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
14243
14244 // One unicast probe conflict
14245
14246 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
14247 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
14248 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
14249
14250 // One multicast and one unicast probe conflict
14251
14252 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
14253 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
14254 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
14255 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
14256 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
14257 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14258 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
14259 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
14260 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14261
14262 // Two multicast probe conflicts
14263
14264 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
14265 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
14266 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
14267 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
14268 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
14269 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14270 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
14271 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
14272 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14273 };
14274
14275 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14276
14277 typedef struct
14278 {
14279 DNSServiceRef registration; // Test service registration.
14280 NanoTime64 testStartTime; // Test's start time.
14281 NanoTime64 startTime; // Current test case's start time.
14282 MDNSColliderRef collider; // mDNS collider object.
14283 CFMutableArrayRef results; // Array of test case results.
14284 char * serviceName; // Test service's instance name as a string. (malloced)
14285 char * serviceType; // Test service's service type as a string. (malloced)
14286 uint8_t * recordName; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14287 unsigned int testCaseIndex; // Index of the current test case.
14288 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
14289 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14290 OutputFormatType outputFormat; // Format of test report output.
14291 Boolean registered; // True if the test service instance is currently registered.
14292 Boolean testFailed; // True if at least one test case failed.
14293
14294 } ProbeConflictTestContext;
14295
14296 static void DNSSD_API
14297 _ProbeConflictTestRegisterCallback(
14298 DNSServiceRef inSDRef,
14299 DNSServiceFlags inFlags,
14300 DNSServiceErrorType inError,
14301 const char * inName,
14302 const char * inType,
14303 const char * inDomain,
14304 void * inContext );
14305 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
14306 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
14307 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
14308 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
14309
14310 static void ProbeConflictTestCmd( void )
14311 {
14312 OSStatus err;
14313 ProbeConflictTestContext * context;
14314 const char * serviceName;
14315 char tag[ 6 + 1 ];
14316
14317 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
14318 require_action( context, exit, err = kNoMemoryErr );
14319
14320 if( gProbeConflictTest_Interface )
14321 {
14322 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
14323 require_noerr_quiet( err, exit );
14324 }
14325 else
14326 {
14327 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14328 require_noerr_quiet( err, exit );
14329 }
14330
14331 if( gProbeConflictTest_OutputFilePath )
14332 {
14333 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
14334 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
14335 }
14336
14337 err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
14338 require_noerr_quiet( err, exit );
14339
14340 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
14341 require_action( context->results, exit, err = kNoMemoryErr );
14342
14343 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
14344
14345 ASPrintF( &context->serviceType, "_pctest-%s._udp",
14346 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14347 require_action( context->serviceType, exit, err = kNoMemoryErr );
14348
14349 context->testStartTime = NanoTimeGetCurrent();
14350 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
14351 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
14352 require_noerr( err, exit );
14353
14354 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
14355 require_noerr( err, exit );
14356
14357 dispatch_main();
14358
14359 exit:
14360 exit( 1 );
14361 }
14362
14363 //===========================================================================================================================
14364 // _ProbeConflictTestRegisterCallback
14365 //===========================================================================================================================
14366
14367 static void DNSSD_API
14368 _ProbeConflictTestRegisterCallback(
14369 DNSServiceRef inSDRef,
14370 DNSServiceFlags inFlags,
14371 DNSServiceErrorType inError,
14372 const char * inName,
14373 const char * inType,
14374 const char * inDomain,
14375 void * inContext )
14376 {
14377 OSStatus err;
14378 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14379
14380 Unused( inSDRef );
14381 Unused( inType );
14382 Unused( inDomain );
14383
14384 err = inError;
14385 require_noerr( err, exit );
14386
14387 if( !context->registered )
14388 {
14389 if( inFlags & kDNSServiceFlagsAdd )
14390 {
14391 uint8_t * ptr;
14392 size_t recordNameLen;
14393 unsigned int len;
14394 uint8_t name[ kDomainNameLengthMax ];
14395
14396 context->registered = true;
14397
14398 FreeNullSafe( context->serviceName );
14399 context->serviceName = strdup( inName );
14400 require_action( context->serviceName, exit, err = kNoMemoryErr );
14401
14402 err = DomainNameFromString( name, context->serviceName, NULL );
14403 require_noerr( err, exit );
14404
14405 err = DomainNameAppendString( name, context->serviceType, NULL );
14406 require_noerr( err, exit );
14407
14408 err = DomainNameAppendString( name, "local", NULL );
14409 require_noerr( err, exit );
14410
14411 ForgetMem( &context->recordName );
14412 err = DomainNameDup( name, &context->recordName, &recordNameLen );
14413 require_noerr( err, exit );
14414 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14415
14416 // Make the first label all caps so that it's easier to spot in system logs.
14417
14418 ptr = context->recordName;
14419 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
14420
14421 err = _ProbeConflictTestStartNextTest( context );
14422 require_noerr( err, exit );
14423 }
14424 }
14425 else
14426 {
14427 if( !( inFlags & kDNSServiceFlagsAdd ) )
14428 {
14429 context->registered = false;
14430 err = _ProbeConflictTestStopCurrentTest( context, true );
14431 require_noerr( err, exit );
14432 }
14433 }
14434 err = kNoErr;
14435
14436 exit:
14437 if( err ) exit( 1 );
14438 }
14439
14440 //===========================================================================================================================
14441 // _ProbeConflictTestColliderStopHandler
14442 //===========================================================================================================================
14443
14444 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
14445 {
14446 OSStatus err;
14447 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14448
14449 err = inError;
14450 require_noerr_quiet( err, exit );
14451
14452 ForgetCF( &context->collider );
14453
14454 err = _ProbeConflictTestStopCurrentTest( context, false );
14455 require_noerr( err, exit );
14456
14457 err = _ProbeConflictTestStartNextTest( context );
14458 require_noerr( err, exit );
14459
14460 exit:
14461 if( err ) exit( 1 );
14462 }
14463
14464 //===========================================================================================================================
14465 // _ProbeConflictTestStartNextTest
14466 //===========================================================================================================================
14467
14468 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
14469 {
14470 OSStatus err;
14471 const ProbeConflictTestCase * testCase;
14472
14473 check( !inContext->collider );
14474
14475 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
14476 {
14477 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14478 }
14479 else
14480 {
14481 _ProbeConflictTestFinalizeAndExit( inContext );
14482 }
14483
14484 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
14485 require_noerr( err, exit );
14486
14487 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
14488 require_noerr( err, exit );
14489
14490 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
14491 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
14492 require_noerr( err, exit );
14493
14494 MDNSColliderSetProtocols( inContext->collider, kMDNSColliderProtocol_IPv4 );
14495 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
14496 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
14497
14498 inContext->startTime = NanoTimeGetCurrent();
14499 err = MDNSColliderStart( inContext->collider );
14500 require_noerr( err, exit );
14501
14502 exit:
14503 return( err );
14504 }
14505
14506 //===========================================================================================================================
14507 // _ProbeConflictTestStopCurrentTest
14508 //===========================================================================================================================
14509
14510 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14511 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14512 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14513 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14514 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14515 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14516
14517 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
14518 {
14519 OSStatus err;
14520 const ProbeConflictTestCase * testCase;
14521 NanoTime64 now;
14522 Boolean passed;
14523 char startTime[ 32 ];
14524 char endTime[ 32 ];
14525
14526 now = NanoTimeGetCurrent();
14527
14528 if( inContext->collider )
14529 {
14530 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
14531 MDNSColliderStop( inContext->collider );
14532 CFRelease( inContext->collider );
14533 inContext->collider = NULL;
14534 }
14535
14536 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14537 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
14538 if( !passed ) inContext->testFailed = true;
14539
14540 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
14541 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14542
14543 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
14544 "{"
14545 "%kO=%s" // description
14546 "%kO=%b" // expectedRename
14547 "%kO=%s" // startTime
14548 "%kO=%s" // endTime
14549 "%kO=%s" // serviceName
14550 "%kO=%b" // passed
14551 "}",
14552 kProbeConflictTestCaseResultKey_Description, testCase->description,
14553 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
14554 kProbeConflictTestCaseResultKey_StartTime, startTime,
14555 kProbeConflictTestCaseResultKey_EndTime, endTime,
14556 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
14557 kProbeConflictTestCaseResultKey_Passed, passed );
14558 require_noerr( err, exit );
14559
14560 ++inContext->testCaseIndex;
14561
14562 exit:
14563 return( err );
14564 }
14565
14566 //===========================================================================================================================
14567 // _ProbeConflictTestFinalizeAndExit
14568 //===========================================================================================================================
14569
14570 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14571 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14572 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14573 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14574 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14575
14576 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
14577 {
14578 OSStatus err;
14579 CFPropertyListRef plist;
14580 NanoTime64 now;
14581 char startTime[ 32 ];
14582 char endTime[ 32 ];
14583
14584 now = NanoTimeGetCurrent();
14585
14586 check( !inContext->collider );
14587
14588 _NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
14589 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14590
14591 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
14592 "{"
14593 "%kO=%s" // startTime
14594 "%kO=%s" // endTime
14595 "%kO=%s" // serviceType
14596 "%kO=%O" // results
14597 "%kO=%b" // passed
14598 "}",
14599 kProbeConflictTestReportKey_StartTime, startTime,
14600 kProbeConflictTestReportKey_EndTime, endTime,
14601 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
14602 kProbeConflictTestReportKey_Results, inContext->results,
14603 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
14604 require_noerr( err, exit );
14605 ForgetCF( &inContext->results );
14606
14607 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
14608 CFRelease( plist );
14609 require_noerr( err, exit );
14610
14611 exit( inContext->testFailed ? 2 : 0 );
14612
14613 exit:
14614 ErrQuit( 1, "error: %#m\n", err );
14615 }
14616
14617 //===========================================================================================================================
14618 // ExpensiveConstrainedsTestCmd
14619 //===========================================================================================================================
14620
14621 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14622 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14623 #define LOOPBACK_INTERFACE_NAME "lo0"
14624 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14625 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14626 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14627 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14628 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14629
14630 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14631 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14632 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14633 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14634
14635 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14636 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14637 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14638 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14639 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14640 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14641 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14642 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14643 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14644 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14645
14646 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14647 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14648 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14649 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14650 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14651 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14652 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14653 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14654
14655 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14656 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14657 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14658 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14659 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14660
14661 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14662 enum ExpensiveConstrainedTestState
14663 {
14664 TEST_BEGIN,
14665 TEST_EXPENSIVE_PREPARE,
14666 TEST_EXPENSIVE, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14667 TEST_CONSTRAINED_PREPARE,
14668 TEST_CONSTRAINED, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14669 TEST_EXPENSIVE_CONSTRAINED_PREPARE,
14670 TEST_EXPENSIVE_CONSTRAINED, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14671 TEST_FAILED,
14672 TEST_SUCCEEDED
14673 };
14674 enum ExpensiveConstrainedTestOperation
14675 {
14676 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.
14677 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.
14678 NO_UPDATE // no status update notification
14679 };
14680
14681 typedef struct
14682 {
14683 uint32_t subtestIndex; // The index of parameter for the subtest
14684 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
14685 const char * name; // Hostname to resolve.
14686 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
14687 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
14688 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
14689 char ifName[IFNAMSIZ]; // Interface name for the given interface index.
14690 dispatch_source_t timer; // The test will check if the current behavior is valid, which is called by
14691 // the timer per 2s.
14692 pid_t serverPID;
14693 Boolean isExpensivePrev; // If the interface is expensive in the previous test step.
14694 Boolean isExpensiveNow; // If the interface is expensive now.
14695 Boolean isConstrainedPrev; // If the interface is constrained in the previous test step.
14696 Boolean isConstrainedNow; // If the interface is constrained now.
14697 Boolean startFromExpensive; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14698 uint8_t numOfRetries; // the number of retries we can have if the test fail
14699 struct timeval updateTime; // The time when interface status(expensive or constrained) is changed.
14700 struct timeval notificationTime; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14701 uint32_t counter; // To record how many times the test has repeated.
14702 enum ExpensiveConstrainedTestState state; // The current test state.
14703 enum ExpensiveConstrainedTestOperation expectedOperation; // the test expects this kind of notification
14704 enum ExpensiveConstrainedTestOperation operation; // represents what notification the callback function gets.
14705
14706 NanoTime64 testReport_startTime; // when the entire test starts
14707 CFMutableArrayRef subtestReport; // stores the log message for every subtest
14708 NanoTime64 subtestReport_startTime; // when the subtest starts
14709 CFMutableArrayRef subtestProgress; // one test iteration
14710 NanoTime64 subtestProgress_startTime; // when the test iteration starts
14711 CFMutableArrayRef subtestProgress_callBack; // array of ADD/REMOVE events
14712 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14713 OutputFormatType outputFormat; // Format of test report output.
14714 } ExpensiveConstrainedContext;
14715
14716 // structure that controls how the subtest is run
14717 typedef struct
14718 {
14719 const char *qname; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14720 Boolean deny_expensive; // if the query should avoid using expensive interface
14721 Boolean deny_constrained; // if the query should avoid using constrained interface
14722 Boolean start_from_expensive; // if the query should starts from using an expensive interface
14723 Boolean ipv4_query; // only allow IPv4 query
14724 Boolean ipv6_query; // only allow IPv6 query
14725 int8_t test_passed; // if the subtest passes
14726 } ExpensiveConstrainedTestParams;
14727
14728 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams[] =
14729 {
14730 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14731 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14732 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14733 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14734 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14735 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14736 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14737 // IPv4 Only
14738 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14739 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14740 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14741 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14742 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14743 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14744 // IPv6 Only
14745 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14746 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14747 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14748 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14749 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14750 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14751 };
14752
14753 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
14754 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
14755 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
14756 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
14757 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
14758 static void DNSSD_API
14759 ExpensiveConstrainedCallback(
14760 DNSServiceRef inSDRef,
14761 DNSServiceFlags inFlags,
14762 uint32_t inInterfaceIndex,
14763 DNSServiceErrorType inError,
14764 const char * inHostname,
14765 const struct sockaddr * inSockAddr,
14766 uint32_t inTTL,
14767 void * inContext );
14768 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
14769 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
14770 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
14771 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
14772 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
14773 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
14774 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
14775 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
14776 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
14777
14778 //===========================================================================================================================
14779 // ExpensiveConstrainedTestCmd
14780 //===========================================================================================================================
14781
14782 static void ExpensiveConstrainedTestCmd( void )
14783 {
14784 OSStatus err;
14785 dispatch_source_t signalSource = NULL;
14786 ExpensiveConstrainedContext * context = NULL;
14787
14788 // Set up SIGINT handler.
14789 signal( SIGINT, SIG_IGN );
14790 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
14791 require_noerr( err, exit );
14792 dispatch_resume( signalSource );
14793
14794 // create the test context
14795 context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
14796 require_action( context, exit, err = kNoMemoryErr );
14797
14798 // get the command line option
14799 err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
14800 require_noerr_quiet( err, exit );
14801 if ( gExpensiveConstrainedTest_OutputFilePath )
14802 {
14803 context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
14804 require_noerr_quiet( context->outputFilePath, exit );
14805 }
14806
14807 // initialize context
14808 context->subtestIndex = 0;
14809 context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
14810
14811 // initialize the CFArray used to store the log
14812 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14813 context->testReport_startTime = NanoTimeGetCurrent();
14814
14815 // setup local DNS server
14816 ExpensiveConstrainedSetupLocalDNSServer( context );
14817
14818 ExpensiveConstrainedStartTestHandler( context );
14819
14820 dispatch_main();
14821
14822 exit:
14823 exit( 1 );
14824 }
14825
14826 //===========================================================================================================================
14827 // ExpensiveConstrainedSetupLocalDNSServer
14828 //===========================================================================================================================
14829
14830 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
14831 {
14832 pid_t current_pid = getpid();
14833 OSStatus err = SpawnCommand( &context->serverPID, "dnssdutil server -l --follow %d", current_pid );
14834 if (err != 0)
14835 {
14836 FPrintF( stdout, "dnssdutil server -l --follow <PID> failed, error: %d\n", err );
14837 exit( 1 );
14838 }
14839 sleep(2);
14840 }
14841
14842 //===========================================================================================================================
14843 // ExpensiveConstrainedStartTestHandler
14844 //===========================================================================================================================
14845
14846 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
14847 {
14848 // setup 3s timer
14849 ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
14850
14851 // set the event handler for the 3s timer
14852 dispatch_source_set_event_handler( context->timer, ^{
14853 ExpensiveConstrainedTestTimerEventHandler( context );
14854 } );
14855
14856 dispatch_resume( context->timer );
14857 }
14858
14859 //===========================================================================================================================
14860 // ExpensiveConstrainedStartTestHandler
14861 //===========================================================================================================================
14862
14863 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
14864 {
14865 dispatch_cancel( context->timer );
14866 dispatch_release( context->timer );
14867 context->timer = NULL;
14868 }
14869
14870 //===========================================================================================================================
14871 // ExpensiveConstrainedSetupTimer
14872 //===========================================================================================================================
14873
14874 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
14875 {
14876 // set the timer source, the event handler will be called for every "second" seconds
14877 context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
14878 if ( context->timer == NULL )
14879 {
14880 FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14881 exit( 1 );
14882 }
14883 // the first block will be put into the queue "second"s after calling dispatch_resume
14884 dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
14885 (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
14886 }
14887
14888 //===========================================================================================================================
14889 // ExpensiveConstrainedTestTimerEventHandler
14890 //===========================================================================================================================
14891
14892 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
14893 {
14894 OSStatus err;
14895 char buffer[ 1024 ];
14896 const char *errorDescription = NULL;
14897
14898 // do not log the state if we are in transition state
14899 if (context->state != TEST_BEGIN
14900 && context->state != TEST_SUCCEEDED
14901 && context->state != TEST_CONSTRAINED_PREPARE
14902 && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
14903 ExpensiveConstrainedSubtestProgressReport( context );
14904
14905 switch ( context->state ) {
14906 case TEST_BEGIN:
14907 {
14908 ExpensiveConstrainedStopTestHandler( context );
14909
14910 // clear mDNSResponder cache
14911 err = systemf( NULL, "killall -HUP mDNSResponder" );
14912 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14913
14914 // initialize the global parameters
14915 ExpensiveConstrainedInitializeContext( context );
14916
14917 // The local DNS server is set up on the local only interface.
14918 gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
14919 strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
14920
14921 // The local DNS server is unscoped, so we must set our question to unscoped.
14922 context->ifIndex = kDNSServiceInterfaceIndexAny;
14923
14924 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14925 // use the local dns server set up previously to run the test locally.
14926 require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
14927 SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
14928 errorDescription = buffer );
14929
14930 // get the quesion name
14931 context->name = gExpensiveConstrainedTest_Name;
14932
14933 // set the initial state for the interface
14934 context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
14935 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
14936 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14937 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14938
14939 // get question flag
14940 if ( gExpensiveConstrainedTest_DenyExpensive ) context->flags |= kDNSServiceFlagsDenyExpensive;
14941 if ( gExpensiveConstrainedTest_DenyConstrained ) context->flags |= kDNSServiceFlagsDenyConstrained;
14942 if ( gExpensiveConstrainedTest_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
14943 if ( gExpensiveConstrainedTest_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
14944
14945 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14946 #if( TARGET_OS_WATCH )
14947 context->flags |= kDNSServiceFlagsPathEvaluationDone;
14948 #endif
14949
14950 // start the query
14951 DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
14952
14953 // set the initial test status
14954 context->subtestReport_startTime = NanoTimeGetCurrent();
14955 context->subtestProgress_startTime = NanoTimeGetCurrent();
14956 context->state = TEST_EXPENSIVE_PREPARE; // start from expensive test
14957 context->isExpensiveNow = context->startFromExpensive ? true : false;
14958 context->isConstrainedNow = false;
14959 context->expectedOperation = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
14960 context->operation = NO_UPDATE;
14961 context->subtestProgress = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14962 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14963 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14964 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14965
14966 // set the queue where the callback will be called when there is an answer for the query
14967 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
14968 require_noerr( err, test_failed );
14969
14970 ExpensiveConstrainedStartTestHandler( context );
14971 }
14972 break;
14973 case TEST_EXPENSIVE_PREPARE:
14974 require_action( context->isConstrainedNow == false, test_failed,
14975 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14976 errorDescription = buffer );
14977 require_action( context->expectedOperation == context->operation, test_failed,
14978 errorDescription = "Operation is not expected" );
14979
14980 context->subtestProgress_startTime = NanoTimeGetCurrent();
14981 context->state = TEST_EXPENSIVE; // begin to test expensive flag
14982 context->counter = 0; // the number of test repetition that has passed
14983 context->isExpensivePrev = context->isExpensiveNow;
14984 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14985 context->isConstrainedPrev = false; // the interface is currently unconstrained
14986 context->isConstrainedNow = false; // the interface will be unconstrained in the current test
14987 if ( gExpensiveConstrainedTest_DenyExpensive )
14988 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14989 else
14990 context->expectedOperation = NO_UPDATE;
14991 context->operation = NO_UPDATE; // NO_UPDATE means the call back function has not been called
14992 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14993 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14994
14995 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14996 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14997
14998 // record the starting timestamp
14999 gettimeofday( &context->updateTime, NULL );
15000
15001 break;
15002 case TEST_EXPENSIVE:
15003 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
15004 require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
15005 SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
15006 errorDescription = buffer );
15007 // constrained flag is always turned off when testing expensive
15008 require_action( context->isConstrainedNow == false, test_failed,
15009 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
15010 errorDescription = buffer );
15011 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
15012
15013 context->counter++; // one test repetition has passed
15014 if ( context->counter == TEST_REPETITION ) // expensive test finished
15015 {
15016 // prepare to test constrained flag
15017 context->state = TEST_CONSTRAINED_PREPARE;
15018
15019 // reset the interface
15020 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
15021 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15022
15023 context->isExpensiveNow = false;
15024 context->isConstrainedNow = false;
15025 gettimeofday( &context->updateTime, NULL );
15026 }
15027 else
15028 {
15029 context->subtestProgress_startTime = NanoTimeGetCurrent();
15030 context->isExpensivePrev = context->isExpensiveNow;
15031 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
15032 if ( gExpensiveConstrainedTest_DenyExpensive )
15033 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
15034 else
15035 context->expectedOperation = NO_UPDATE;
15036 context->operation = NO_UPDATE;
15037 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15038 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
15039
15040 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
15041 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15042
15043 gettimeofday( &context->updateTime, NULL );
15044 }
15045 break;
15046 case TEST_CONSTRAINED_PREPARE:
15047 // The interface should be inexpensive and unconstrained when the constrained test starts
15048 require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
15049 errorDescription = buffer );
15050 require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
15051 errorDescription = buffer );
15052
15053 context->subtestProgress_startTime = NanoTimeGetCurrent();
15054 context->state = TEST_CONSTRAINED; // constrained interface is now under testing
15055 context->counter = 0;
15056 context->isExpensivePrev = false;
15057 context->isExpensiveNow = false;
15058 context->isConstrainedPrev = false;
15059 context->isConstrainedNow = true; // will set constrained flag on the interface
15060 if ( gExpensiveConstrainedTest_DenyConstrained )
15061 context->expectedOperation = RESULT_RMV;
15062 else
15063 context->expectedOperation = NO_UPDATE;
15064 context->operation = NO_UPDATE;
15065 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15066 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
15067
15068 // change interface to the constrained one
15069 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
15070 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15071
15072 gettimeofday( &context->updateTime, NULL );
15073 break;
15074 case TEST_CONSTRAINED:
15075 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
15076 require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
15077 SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
15078 errorDescription = buffer );
15079 require_action( context->isExpensiveNow == false, test_failed,
15080 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
15081 errorDescription = buffer );
15082 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
15083
15084 context->counter++;
15085 if (context->counter == TEST_REPETITION)
15086 {
15087 // test changing expensive and constrained flags at the same time
15088 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
15089
15090 // reset interface
15091 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
15092 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15093
15094 context->isExpensiveNow = false;
15095 context->isConstrainedNow = false;
15096 gettimeofday( &context->updateTime, NULL );
15097 }
15098 else
15099 {
15100 context->subtestProgress_startTime = NanoTimeGetCurrent();
15101 context->isConstrainedPrev = context->isConstrainedNow;
15102 context->isConstrainedNow = !context->isConstrainedNow; // flip constrained flag
15103 if ( gExpensiveConstrainedTest_DenyConstrained )
15104 context->expectedOperation = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
15105 else
15106 context->expectedOperation = NO_UPDATE;
15107 context->operation = NO_UPDATE;
15108 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15109 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
15110
15111 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
15112 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15113
15114 gettimeofday(&context->updateTime, NULL);
15115 }
15116 break;
15117 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
15118 // The interface should be inexpensive and unconstrained when the constrained test starts
15119 require_action( context->isExpensiveNow == false, test_failed,
15120 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
15121 errorDescription = buffer );
15122 require_action( context->isConstrainedNow == false, test_failed,
15123 SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
15124 errorDescription = buffer );
15125
15126 // now flip expensive and constrained at the same time
15127 context->subtestProgress_startTime = NanoTimeGetCurrent();
15128 context->state = TEST_EXPENSIVE_CONSTRAINED;
15129 context->counter = 0;
15130 context->isExpensivePrev = false;
15131 context->isExpensiveNow = true;
15132 context->isConstrainedPrev = false;
15133 context->isConstrainedNow = true;
15134 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
15135 context->expectedOperation = RESULT_RMV;
15136 else
15137 context->expectedOperation = NO_UPDATE;
15138 context->operation = NO_UPDATE;
15139 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15140 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
15141
15142 err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
15143 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15144
15145 gettimeofday( &context->updateTime, NULL );
15146 break;
15147 case TEST_EXPENSIVE_CONSTRAINED:
15148 // expensive and constrained flag should always be changed
15149 require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
15150 SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
15151 errorDescription = buffer );
15152 require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
15153 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
15154
15155 context->counter++;
15156 if ( context->counter == TEST_REPETITION )
15157 {
15158 context->state = TEST_SUCCEEDED;
15159 }
15160 else
15161 {
15162 context->subtestProgress_startTime = NanoTimeGetCurrent();
15163 context->isExpensivePrev = context->isExpensiveNow;
15164 context->isExpensiveNow = !context->isExpensiveNow;
15165 context->isConstrainedPrev = context->isConstrainedNow;
15166 context->isConstrainedNow = !context->isConstrainedNow;
15167 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
15168 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
15169 else
15170 context->expectedOperation = NO_UPDATE;
15171 context->operation = NO_UPDATE;
15172 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15173 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
15174
15175 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
15176 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
15177
15178 gettimeofday( &context->updateTime, NULL );
15179 }
15180 break;
15181 case TEST_FAILED:
15182 test_failed:
15183 ExpensiveConstrainedSubtestReport( context, errorDescription );
15184 ExpensiveConstrainedStopAndCleanTheTest( context );
15185 if ( context->numOfRetries > 0 )
15186 {
15187 context->state = TEST_BEGIN;
15188 context->numOfRetries--;
15189 break;
15190 }
15191 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
15192 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
15193 {
15194 ExpensiveConstrainedFinalResultReport( context, false );
15195 exit( 2 );
15196 }
15197 if (context->timer == NULL)
15198 {
15199 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
15200 ExpensiveConstrainedFinalResultReport( context, false );
15201 exit( 1 );
15202 }
15203 context->state = TEST_BEGIN;
15204 break;
15205 case TEST_SUCCEEDED:
15206 ExpensiveConstrainedSubtestReport( context, NULL );
15207 ExpensiveConstrainedStopAndCleanTheTest( context );
15208 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
15209 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
15210 {
15211 // all the subtests have been run
15212 Boolean hasFailed = false;
15213 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
15214 hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
15215
15216 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
15217 exit( hasFailed ? 2 : 0 );
15218 }
15219 context->state = TEST_BEGIN;
15220 break;
15221 default:
15222 FPrintF( stdout, "unknown error\n" );
15223 exit( 1 );
15224 }
15225 }
15226
15227 //===========================================================================================================================
15228 // ExpensiveConstrainedCallback
15229 //===========================================================================================================================
15230
15231 static void DNSSD_API
15232 ExpensiveConstrainedCallback(
15233 __unused DNSServiceRef inSDRef,
15234 DNSServiceFlags inFlags,
15235 uint32_t inInterfaceIndex,
15236 DNSServiceErrorType inError,
15237 const char * inHostname,
15238 const struct sockaddr * inSockAddr,
15239 __unused uint32_t inTTL,
15240 void * inContext )
15241 {
15242 ExpensiveConstrainedContext * const context = (ExpensiveConstrainedContext *)inContext;
15243 OSStatus err;
15244 const char * addrStr;
15245 char addrStrBuf[ kSockAddrStringMaxSize ];
15246 char inFlagsDescription[ 128 ];
15247 NanoTime64 now;
15248 char nowTimestamp[ 32 ];
15249
15250 switch ( inError ) {
15251 case kDNSServiceErr_NoError:
15252 case kDNSServiceErr_NoSuchRecord:
15253 break;
15254
15255 case kDNSServiceErr_Timeout:
15256 Exit( kExitReason_Timeout );
15257
15258 default:
15259 err = inError;
15260 goto exit;
15261 }
15262
15263 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
15264 {
15265 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
15266 err = kTypeErr;
15267 goto exit;
15268 }
15269
15270 if( !inError )
15271 {
15272 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
15273 require_noerr( err, exit );
15274 addrStr = addrStrBuf;
15275 }
15276 else
15277 {
15278 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
15279 }
15280
15281 now = NanoTimeGetCurrent();
15282 _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
15283 SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
15284 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress_callBack,
15285 "{"
15286 "%kO=%s"
15287 "%kO=%s"
15288 "%kO=%s"
15289 "%kO=%lli"
15290 "%kO=%s"
15291 "}",
15292 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP, nowTimestamp,
15293 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME, inHostname,
15294 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS, inFlagsDescription,
15295 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE, (int64_t) inInterfaceIndex,
15296 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS, addrStr
15297 );
15298 require_noerr_quiet( err, exit );
15299
15300 if ( inFlags & kDNSServiceFlagsMoreComing )
15301 return;
15302
15303 if ( inFlags & kDNSServiceFlagsAdd )
15304 context->operation = RESULT_ADD;
15305 else
15306 context->operation = RESULT_RMV;
15307
15308 gettimeofday(&context->notificationTime, NULL);
15309 exit:
15310 if( err ) exit( 1 );
15311 }
15312
15313 //===========================================================================================================================
15314 // ExpensiveConstrainedInitializeContext
15315 //===========================================================================================================================
15316
15317 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
15318 {
15319 // clear the flags of the previous subtest
15320 context->flags = 0;
15321 context->protocols = 0;
15322
15323 // get the parameter for the current subtest
15324 const ExpensiveConstrainedTestParams *param = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
15325 gExpensiveConstrainedTest_Name = param->qname;
15326 gExpensiveConstrainedTest_DenyExpensive = param->deny_expensive;
15327 gExpensiveConstrainedTest_DenyConstrained = param->deny_constrained;
15328 gExpensiveConstrainedTest_StartFromExpensive = param->start_from_expensive;
15329 gExpensiveConstrainedTest_ProtocolIPv4 = param->ipv4_query;
15330 gExpensiveConstrainedTest_ProtocolIPv6 = param->ipv6_query;
15331 }
15332
15333 //===========================================================================================================================
15334 // ExpensiveConstrainedStopAndCleanTheTest
15335 //===========================================================================================================================
15336
15337 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
15338 {
15339 // Stop the ongoing query
15340 if ( context->opRef != NULL )
15341 DNSServiceRefDeallocate( context->opRef );
15342
15343 context->opRef = NULL;
15344 context->flags = 0;
15345 context->protocols = 0;
15346 }
15347
15348 //===========================================================================================================================
15349 // ExpensiveConstrainedSubtestProgressReport
15350 //===========================================================================================================================
15351
15352 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
15353 {
15354 OSStatus err;
15355 NanoTime64 now;
15356 char startTime[ 32 ];
15357 char endTime[ 32 ];
15358 char expensive[ 32 ];
15359 char constrained[ 32 ];
15360
15361 now = NanoTimeGetCurrent();
15362 _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
15363 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15364
15365 snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
15366 snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
15367
15368 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
15369 "{"
15370 "%kO=%s"
15371 "%kO=%s"
15372 "%kO=%s"
15373 "%kO=%s"
15374 "%kO=%s"
15375 "%kO=%s"
15376 "%kO=%s"
15377 "%kO=%O"
15378 "}",
15379 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME, startTime,
15380 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME, endTime,
15381 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE, ExpensiveConstrainedStateString(context->state),
15382 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT, ExpensiveConstrainedOperationString(context->expectedOperation),
15383 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT, ExpensiveConstrainedOperationString(context->operation),
15384 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW, expensive,
15385 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW, constrained,
15386 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK, context->subtestProgress_callBack
15387 );
15388 require_noerr( err, exit );
15389 ForgetCF( &context->subtestProgress_callBack );
15390 return;
15391
15392 exit:
15393 ErrQuit( 1, "error: %#m\n", err );
15394 }
15395
15396 //===========================================================================================================================
15397 // ExpensiveConstrainedFinalSubtestReport
15398 //===========================================================================================================================
15399
15400 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
15401 {
15402 OSStatus err;
15403 NanoTime64 now;
15404 char startTime[ 32 ];
15405 char endTime[ 32 ];
15406 char flagDescription[ 1024 ];
15407
15408 now = NanoTimeGetCurrent();
15409 _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
15410 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15411 SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
15412
15413 if (error_description != NULL)
15414 {
15415 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15416 "{"
15417 "%kO=%s"
15418 "%kO=%s"
15419 "%kO=%s"
15420 "%kO=%s"
15421 "%kO=%s"
15422 "%kO=%lli"
15423 "%kO=%s"
15424 "%kO=%O"
15425 "%kO=%s"
15426 "%kO=%O"
15427 "}",
15428 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15429 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15430 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15431 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15432 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15433 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15434 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15435 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Fail" ),
15436 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR, error_description,
15437 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15438 );
15439 }
15440 else
15441 {
15442 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15443 "{"
15444 "%kO=%s"
15445 "%kO=%s"
15446 "%kO=%s"
15447 "%kO=%s"
15448 "%kO=%s"
15449 "%kO=%lli"
15450 "%kO=%s"
15451 "%kO=%O"
15452 "%kO=%O"
15453 "}",
15454 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15455 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15456 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15457 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15458 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15459 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15460 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15461 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Pass" ),
15462 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15463 );
15464 }
15465
15466 require_noerr( err, exit );
15467 ForgetCF( &context->subtestProgress );
15468 return;
15469 exit:
15470 ErrQuit( 1, "error: %#m\n", err );
15471 }
15472
15473 //===========================================================================================================================
15474 // ExpensiveConstrainedFinalResultReport
15475 //===========================================================================================================================
15476
15477 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
15478 {
15479 OSStatus err;
15480 CFPropertyListRef plist;
15481 NanoTime64 now;
15482 char startTime[ 32 ];
15483 char endTime[ 32 ];
15484
15485 now = NanoTimeGetCurrent();
15486 _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
15487 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15488
15489 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15490 "{"
15491 "%kO=%s"
15492 "%kO=%s"
15493 "%kO=%b"
15494 "%kO=%O"
15495 "}",
15496 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME, startTime,
15497 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME, endTime,
15498 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED, allPassed,
15499 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT, context->subtestReport
15500 );
15501 require_noerr( err, exit );
15502 ForgetCF( &context->subtestReport );
15503
15504 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15505 CFRelease( plist );
15506 require_noerr( err, exit );
15507
15508 return;
15509 exit:
15510 ErrQuit( 1, "error: %#m\n", err );
15511 }
15512
15513 //===========================================================================================================================
15514 // ExpensiveConstrainedProtocolString
15515 //===========================================================================================================================
15516
15517 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
15518 {
15519 const char *str = NULL;
15520 switch ( protocol ) {
15521 case kDNSServiceProtocol_IPv4:
15522 str = "IPv4";
15523 break;
15524 case kDNSServiceProtocol_IPv6:
15525 str = "IPv6";
15526 break;
15527 case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
15528 str = "IPv4 & IPv6";
15529 break;
15530 default:
15531 break;
15532 }
15533 return str;
15534 }
15535
15536 //===========================================================================================================================
15537 // ExpensiveConstrainedStateString
15538 //===========================================================================================================================
15539
15540 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
15541 {
15542 const char *str = NULL;
15543 switch ( state ) {
15544 case TEST_BEGIN:
15545 str = "TEST_BEGIN";
15546 break;
15547 case TEST_EXPENSIVE_PREPARE:
15548 str = "TEST_EXPENSIVE_PREPARE";
15549 break;
15550 case TEST_EXPENSIVE:
15551 str = "TEST_EXPENSIVE";
15552 break;
15553 case TEST_CONSTRAINED_PREPARE:
15554 str = "TEST_CONSTRAINED_PREPARE";
15555 break;
15556 case TEST_CONSTRAINED:
15557 str = "TEST_CONSTRAINED";
15558 break;
15559 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
15560 str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15561 break;
15562 case TEST_EXPENSIVE_CONSTRAINED:
15563 str = "TEST_EXPENSIVE_CONSTRAINED";
15564 break;
15565 case TEST_FAILED:
15566 str = "TEST_FAILED";
15567 break;
15568 case TEST_SUCCEEDED:
15569 str = "TEST_SUCCEEDED";
15570 break;
15571 default:
15572 str = "UNKNOWN";
15573 break;
15574 }
15575
15576 return str;
15577 }
15578
15579 //===========================================================================================================================
15580 // ExpensiveConstrainedOperationString
15581 //===========================================================================================================================
15582
15583 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
15584 {
15585 const char *str = NULL;
15586 switch ( operation ) {
15587 case RESULT_ADD:
15588 str = "RESULT_ADD";
15589 break;
15590 case RESULT_RMV:
15591 str = "RESULT_RMV";
15592 break;
15593 case NO_UPDATE:
15594 str = "NO_UPDATE";
15595 break;
15596 default:
15597 str = "UNKNOWN";
15598 break;
15599 }
15600 return str;
15601 }
15602
15603 //===========================================================================================================================
15604 // expensiveConstrainedEndsWith
15605 //===========================================================================================================================
15606 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
15607 {
15608 if ( !str || !suffix )
15609 return false;
15610 size_t lenstr = strlen( str );
15611 size_t lensuffix = strlen( suffix );
15612 if ( lensuffix > lenstr )
15613 return false;
15614 return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
15615 }
15616
15617 //===========================================================================================================================
15618 // RegistrationTestCmd
15619 //===========================================================================================================================
15620
15621 typedef struct RegistrationSubtest RegistrationSubtest;
15622
15623 typedef struct
15624 {
15625 CFMutableArrayRef subtestReports; // Array of subtest reports.
15626 dispatch_source_t timer; // Timer to enforce subtest durations.
15627 dispatch_source_t sigSourceINT; // SIGINT signal handler for a clean test exit.
15628 dispatch_source_t sigSourceTERM; // SIGTERM signal handler for a clean test exit.
15629 RegistrationSubtest * subtest; // Current subtest.
15630 char * outputFilePath; // Path of test result output file. If NULL, stdout will be used.
15631 OutputFormatType outputFormat; // Format of test results output.
15632 CFStringRef computerNamePrev; // Previous ComputerName.
15633 CFStringRef localHostNamePrev; // Previous LocalHostName.
15634 NanoTime64 startTime; // Test's start time.
15635 char * computerName; // Temporary ComputerName to set during testing. (malloc'd)
15636 char * localHostName; // Temporary LocalHostName to set during testing. (malloc'd)
15637 CFStringEncoding computerNamePrevEncoding; // Previous ComputerName's encoding.
15638 int subtestIndex; // Index of current subtest.
15639 Boolean computerNameSet; // True if a temporary ComputerName was set.
15640 Boolean localHostNameSet; // True if a temporary LocalHostName was set.
15641 Boolean failed; // True if at least one non-skipped subtest failed.
15642 Boolean forBATS; // True if the test is running in a BATS environment.
15643
15644 } RegistrationTest;
15645
15646 typedef enum
15647 {
15648 kRegistrationInterfaceSet_Null = 0,
15649 kRegistrationInterfaceSet_All = 1,
15650 kRegistrationInterfaceSet_AllPlusAWDL = 2,
15651 kRegistrationInterfaceSet_LoopbackOnly = 3,
15652 kRegistrationInterfaceSet_AWDLOnly = 4
15653
15654 } RegistrationInterfaceSet;
15655
15656 typedef struct
15657 {
15658 RegistrationInterfaceSet interfaceSet; // Interfaces to register the service over.
15659 Boolean useDefaultName; // True if registration is to use the default service name.
15660 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15661
15662 } RegistrationSubtestParams;
15663
15664 static const RegistrationSubtestParams kRegistrationSubtestParams[] =
15665 {
15666 { kRegistrationInterfaceSet_All, true, false },
15667 { kRegistrationInterfaceSet_All, false, false },
15668 { kRegistrationInterfaceSet_AllPlusAWDL, true, false },
15669 { kRegistrationInterfaceSet_AllPlusAWDL, false, false },
15670 { kRegistrationInterfaceSet_LoopbackOnly, true, false },
15671 { kRegistrationInterfaceSet_LoopbackOnly, false, false },
15672 { kRegistrationInterfaceSet_AWDLOnly, true, false },
15673 { kRegistrationInterfaceSet_AWDLOnly, false, false },
15674 { kRegistrationInterfaceSet_All, true, true },
15675 { kRegistrationInterfaceSet_All, false, true },
15676 { kRegistrationInterfaceSet_AllPlusAWDL, true, true },
15677 { kRegistrationInterfaceSet_AllPlusAWDL, false, true },
15678 { kRegistrationInterfaceSet_LoopbackOnly, true, true },
15679 { kRegistrationInterfaceSet_LoopbackOnly, false, true },
15680 { kRegistrationInterfaceSet_AWDLOnly, true, true },
15681 { kRegistrationInterfaceSet_AWDLOnly, false, true }
15682 };
15683
15684 typedef struct
15685 {
15686 NanoTime64 browseResultTime; // Per-interface browse result time.
15687 NanoTime64 querySRVResultTime; // Per-interface SRV record query result time.
15688 NanoTime64 queryTXTResultTime; // Per-interface TXT record query result time.
15689
15690 } RegistrationResultTimes;
15691
15692 typedef struct
15693 {
15694 MDNSInterfaceItem base; // Underlying MDNSInterface linked-list item.
15695 RegistrationResultTimes times; // Per-interface result times.
15696
15697 } RegistrationInterfaceItem;
15698
15699 struct RegistrationSubtest
15700 {
15701 DNSServiceRef registration; // DNS-SD service registration.
15702 DNSServiceRef connection; // Shared DNS-SD connection.
15703 DNSServiceRef browse; // DNS-SD browse for service's type.
15704 DNSServiceRef querySRV; // DNS-SD query request for service's SRV record.
15705 DNSServiceRef queryTXT; // DNS-SD query request for service's TXT record.
15706 CFMutableArrayRef unexpected; // Array of unexpected registration, browse, and query results.
15707 #if( TARGET_OS_WATCH )
15708 CFMutableArrayRef ignored; // Array of unexpected, but ignored, browse and query results.
15709 #endif
15710 const char * serviceName; // Service's name.
15711 char * serviceNameCustom; // Service's name if using a custom name. (malloc'd)
15712 char * serviceType; // Service's service type. (malloc'd)
15713 size_t serviceTypeLen; // C string length of service's service type.
15714 char * serviceFQDN; // Service's FQDN, i.e., name of its SRV and TXT records.
15715 uint8_t * txtPtr; // Pointer to service's TXT record data. (malloc'd)
15716 size_t txtLen; // Length of service's TXT record data.
15717 RegistrationInterfaceItem * ifList; // If ifIndex == 0, interfaces that service should register over.
15718 RegistrationResultTimes ifTimes; // If ifIndex != 0, result times for interface with that index.
15719 RegistrationTest * test; // Pointer to parent test.
15720 NanoTime64 startTime; // Subtest's start time.
15721 char * description; // Subtest's description. (malloc'd)
15722 uint32_t ifIndex; // Interface index used for service registration.
15723 uint16_t port; // Service's port number.
15724 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15725 Boolean includeAWDL; // True if the IncludeAWDL flag was used during registration.
15726 Boolean ifIsAWDL; // True if ifIndex is the index of an AWDL interface.
15727 Boolean skipped; // True if this subtest is to be skipped.
15728 Boolean registered; // True if the test service was successfully registered.
15729 Boolean useDefaultName; // True if the service is to use the default service name.
15730 };
15731
15732 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest );
15733 static void _RegistrationTestFree( RegistrationTest *inTest );
15734 static void _RegistrationTestBegin( void *inContext );
15735 static void _RegistrationTestProceed( RegistrationTest *inTest );
15736 static OSStatus _RegistrationTestStart( RegistrationTest *inTest );
15737 static void _RegistrationTestStop( RegistrationTest *inTest );
15738 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15739 static OSStatus
15740 _RegistrationTestStartSubtest(
15741 RegistrationTest * inTest,
15742 const RegistrationSubtestParams * inParams,
15743 Boolean * outSkipped );
15744 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest );
15745 static void _RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
15746 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
15747 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
15748 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest );
15749 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest );
15750 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15751 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
15752 static OSStatus
15753 _RegistrationTestCreateRandomTXTRecord(
15754 size_t inMinLen,
15755 size_t inMaxLen,
15756 uint8_t ** outTXTPtr,
15757 size_t * outTXTLen );
15758 static void DNSSD_API
15759 _RegistrationSubtestRegisterCallback(
15760 DNSServiceRef inSDRef,
15761 DNSServiceFlags inFlags,
15762 DNSServiceErrorType inError,
15763 const char * inName,
15764 const char * inType,
15765 const char * inDomain,
15766 void * inContext );
15767 static void DNSSD_API
15768 _RegistrationSubtestBrowseCallback(
15769 DNSServiceRef inSDRef,
15770 DNSServiceFlags inFlags,
15771 uint32_t inIfIndex,
15772 DNSServiceErrorType inError,
15773 const char * inServiceName,
15774 const char * inServiceType,
15775 const char * inDomain,
15776 void * inContext );
15777 static void DNSSD_API
15778 _RegistrationSubtestQueryCallback(
15779 DNSServiceRef inSDRef,
15780 DNSServiceFlags inFlags,
15781 uint32_t inIfIndex,
15782 DNSServiceErrorType inError,
15783 const char * inName,
15784 uint16_t inType,
15785 uint16_t inClass,
15786 uint16_t inRDataLen,
15787 const void * inRDataPtr,
15788 uint32_t inTTL,
15789 void * inContext );
15790 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
15791 static RegistrationResultTimes *
15792 _RegistrationSubtestGetInterfaceResultTimes(
15793 RegistrationSubtest * inSubtest,
15794 uint32_t inIfIndex,
15795 Boolean * outIsAWDL );
15796 static void _RegistrationTestTimerHandler( void *inContext );
15797 #if( TARGET_OS_WATCH )
15798 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName );
15799 #endif
15800
15801 static void RegistrationTestCmd( void )
15802 {
15803 OSStatus err;
15804 RegistrationTest * test;
15805
15806 err = _RegistrationTestCreate( &test );
15807 require_noerr( err, exit );
15808
15809 if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
15810 if( gRegistrationTest_OutputFilePath )
15811 {
15812 test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
15813 require_action( test->outputFilePath, exit, err = kNoMemoryErr );
15814 }
15815
15816 err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
15817 require_noerr_quiet( err, exit );
15818
15819 dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
15820 dispatch_main();
15821
15822 exit:
15823 if( test ) _RegistrationTestFree( test );
15824 ErrQuit( 1, "error: %#m\n", err );
15825 }
15826
15827 //===========================================================================================================================
15828
15829 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest )
15830 {
15831 OSStatus err;
15832 RegistrationTest * obj;
15833
15834 obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
15835 require_action( obj, exit, err = kNoMemoryErr );
15836
15837 obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15838 require_action( obj->subtestReports, exit, err = kNoMemoryErr );
15839
15840 *outTest = obj;
15841 obj = NULL;
15842 err = kNoErr;
15843
15844 exit:
15845 if( obj ) _RegistrationTestFree( obj );
15846 return( err );
15847 }
15848
15849 //===========================================================================================================================
15850
15851 static void _RegistrationTestFree( RegistrationTest *inTest )
15852 {
15853 check( !inTest->timer );
15854 check( !inTest->sigSourceINT );
15855 check( !inTest->sigSourceTERM );
15856 check( !inTest->computerNameSet );
15857 check( !inTest->localHostNameSet );
15858 check( !inTest->subtest );
15859 ForgetCF( &inTest->subtestReports );
15860 ForgetMem( &inTest->outputFilePath );
15861 ForgetCF( &inTest->computerNamePrev );
15862 ForgetCF( &inTest->localHostNamePrev );
15863 ForgetMem( &inTest->computerName );
15864 ForgetMem( &inTest->localHostName );
15865 }
15866
15867 //===========================================================================================================================
15868
15869 static void _RegistrationTestBegin( void *inContext )
15870 {
15871 _RegistrationTestProceed( (RegistrationTest *) inContext );
15872 }
15873
15874 //===========================================================================================================================
15875
15876 static void _RegistrationTestProceed( RegistrationTest *inTest )
15877 {
15878 OSStatus err;
15879 Boolean skippedSubtest;
15880
15881 do
15882 {
15883 int subtestIndex;
15884
15885 if( !inTest->startTime )
15886 {
15887 err = _RegistrationTestStart( inTest );
15888 require_noerr_quiet( err, exit );
15889
15890 inTest->startTime = NanoTimeGetCurrent();
15891 }
15892 else
15893 {
15894 err = _RegistrationTestEndSubtest( inTest );
15895 require_noerr( err, exit );
15896
15897 ++inTest->subtestIndex;
15898 }
15899
15900 subtestIndex = inTest->subtestIndex;
15901 if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
15902 {
15903 err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
15904 require_noerr_quiet( err, exit );
15905 }
15906 else
15907 {
15908 _RegistrationTestEnd( inTest );
15909 }
15910
15911 } while( skippedSubtest );
15912
15913 exit:
15914 if( err ) _RegistrationTestExit( inTest, err );
15915 }
15916
15917 //===========================================================================================================================
15918
15919 static void _RegistrationTestSignalHandler( void *inContext );
15920
15921 static OSStatus _RegistrationTestStart( RegistrationTest *inTest )
15922 {
15923 OSStatus err;
15924 char tag[ 6 + 1 ];
15925
15926 // Save original ComputerName and LocalHostName.
15927
15928 check( !inTest->computerNamePrev );
15929 inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
15930 err = map_scerror( inTest->computerNamePrev );
15931 require_noerr( err, exit );
15932
15933 check( !inTest->localHostNamePrev );
15934 inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
15935 err = map_scerror( inTest->localHostNamePrev );
15936 require_noerr( err, exit );
15937
15938 // Generate a unique test ComputerName.
15939
15940 check( !inTest->computerName );
15941 ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
15942 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15943 require_action( inTest->computerName, exit, err = kNoMemoryErr );
15944
15945 // Generate a unique test LocalHostName.
15946
15947 check( !inTest->localHostName );
15948 ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
15949 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15950 require_action( inTest->localHostName, exit, err = kNoMemoryErr );
15951
15952 // Set up SIGINT signal handler.
15953
15954 signal( SIGINT, SIG_IGN );
15955 check( !inTest->sigSourceINT );
15956 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
15957 &inTest->sigSourceINT );
15958 require_noerr( err, exit );
15959 dispatch_resume( inTest->sigSourceINT );
15960
15961 // Set up SIGTERM signal handler.
15962
15963 signal( SIGTERM, SIG_IGN );
15964 check( !inTest->sigSourceTERM );
15965 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
15966 &inTest->sigSourceTERM );
15967 require_noerr( err, exit );
15968 dispatch_resume( inTest->sigSourceTERM );
15969
15970 // Set test ComputerName.
15971
15972 check( !inTest->computerNameSet );
15973 err = _SetComputerNameWithUTF8CString( inTest->computerName );
15974 require_noerr( err, exit );
15975 inTest->computerNameSet = true;
15976
15977 // Set test LocalHostName.
15978
15979 check( !inTest->localHostNameSet );
15980 err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
15981 require_noerr( err, exit );
15982 inTest->localHostNameSet = true;
15983
15984 exit:
15985 if( err ) _RegistrationTestStop( inTest );
15986 return( err );
15987 }
15988
15989 static void _RegistrationTestSignalHandler( void *inContext )
15990 {
15991 RegistrationTest * const test = (RegistrationTest *) inContext;
15992
15993 FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15994
15995 _RegistrationTestExit( test, kCanceledErr );
15996 }
15997
15998 //===========================================================================================================================
15999
16000 static void _RegistrationTestStop( RegistrationTest *inTest )
16001 {
16002 OSStatus err;
16003
16004 dispatch_source_forget( &inTest->timer );
16005 dispatch_source_forget( &inTest->sigSourceINT );
16006 dispatch_source_forget( &inTest->sigSourceTERM );
16007 _RegistrationSubtestForget( &inTest->subtest );
16008 if( inTest->computerNameSet )
16009 {
16010 err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
16011 check_noerr( err );
16012 if( !err ) inTest->computerNameSet = false;
16013 }
16014 if( inTest->localHostNameSet )
16015 {
16016 err = _SetLocalHostName( inTest->localHostNamePrev );
16017 check_noerr( err );
16018 if( !err ) inTest->localHostNameSet = false;
16019 }
16020 }
16021
16022 //===========================================================================================================================
16023
16024 #define kRegistrationTestSubtestDurationSecs 5
16025
16026 static OSStatus
16027 _RegistrationTestStartSubtest(
16028 RegistrationTest * inTest,
16029 const RegistrationSubtestParams * inParams,
16030 Boolean * outSkipped )
16031 {
16032 OSStatus err;
16033 RegistrationSubtest * subtest;
16034 const char * interfaceDesc;
16035 DNSServiceFlags flags;
16036 char tag[ 6 + 1 ];
16037
16038 subtest = NULL;
16039 err = _RegistrationSubtestCreate( &subtest );
16040 require_noerr( err, exit );
16041
16042 subtest->test = inTest;
16043 subtest->useDefaultName = inParams->useDefaultName;
16044 subtest->useLODiscovery = inParams->useLODiscovery;
16045
16046 // Determine registration interfaces.
16047
16048 switch( inParams->interfaceSet )
16049 {
16050 case kRegistrationInterfaceSet_All:
16051 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
16052
16053 if( !subtest->useLODiscovery )
16054 {
16055 err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
16056 require_noerr( err, exit );
16057 }
16058 interfaceDesc = "all interfaces (excluding AWDL)";
16059 break;
16060
16061 case kRegistrationInterfaceSet_AllPlusAWDL:
16062 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
16063 subtest->includeAWDL = true;
16064
16065 if( !subtest->useLODiscovery )
16066 {
16067 err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
16068 require_noerr( err, exit );
16069 }
16070 interfaceDesc = "all interfaces (including AWDL)";
16071 break;
16072
16073 case kRegistrationInterfaceSet_LoopbackOnly:
16074 subtest->ifIndex = if_nametoindex( "lo0" );
16075 if( subtest->ifIndex == 0 )
16076 {
16077 FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
16078 err = kNoResourcesErr;
16079 goto exit;
16080 }
16081 interfaceDesc = "loopback interface";
16082 break;
16083
16084 case kRegistrationInterfaceSet_AWDLOnly:
16085 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
16086 if( err == kNotFoundErr )
16087 {
16088 FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
16089 subtest->skipped = true;
16090 err = kNoErr;
16091 }
16092 require_noerr( err, exit );
16093
16094 subtest->ifIsAWDL = true;
16095 interfaceDesc = "AWDL interface";
16096 break;
16097
16098 default:
16099 err = kParamErr;
16100 goto exit;
16101 }
16102
16103 // Create description.
16104
16105 ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
16106 interfaceDesc, subtest->useDefaultName ? "default" : "custom",
16107 subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
16108 require_action( subtest->description, exit, err = kNoMemoryErr );
16109
16110 if( subtest->skipped )
16111 {
16112 subtest->startTime = NanoTimeGetCurrent();
16113 }
16114 else
16115 {
16116 // Generate a service name.
16117
16118 if( subtest->useDefaultName )
16119 {
16120 subtest->serviceName = inTest->computerName;
16121 }
16122 else
16123 {
16124 ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
16125 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
16126 require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
16127
16128 subtest->serviceName = subtest->serviceNameCustom;
16129 }
16130
16131 // Generate a service type.
16132
16133 ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
16134 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
16135 require_action( subtest->serviceType, exit, err = kNoMemoryErr );
16136
16137 subtest->serviceTypeLen = strlen( subtest->serviceType );
16138
16139 // Create SRV and TXT record name FQDN.
16140
16141 ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
16142 require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
16143
16144 // Generate a port number.
16145
16146 subtest->port = (uint16_t) RandomRange( 60000, 65535 );
16147
16148 // Generate TXT record data.
16149
16150 err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
16151 require_noerr( err, exit );
16152
16153 // Register service.
16154
16155 subtest->startTime = NanoTimeGetCurrent();
16156
16157 flags = kDNSServiceFlagsNoAutoRename;
16158 if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
16159 err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
16160 subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
16161 NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
16162 _RegistrationSubtestRegisterCallback, subtest );
16163 require_noerr( err, exit );
16164
16165 err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
16166 require_noerr( err, exit );
16167
16168 // Start timer.
16169
16170 check( !inTest->timer );
16171 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
16172 INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
16173 _RegistrationTestTimerHandler, inTest, &inTest->timer );
16174 require_noerr( err, exit );
16175 dispatch_resume( inTest->timer );
16176 }
16177
16178 *outSkipped = subtest->skipped;
16179
16180 check( !inTest->subtest );
16181 inTest->subtest = subtest;
16182 subtest = NULL;
16183
16184 exit:
16185 _RegistrationSubtestForget( &subtest );
16186 return( err );
16187 }
16188
16189 //===========================================================================================================================
16190
16191 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
16192 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
16193 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
16194 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
16195 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
16196 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
16197 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
16198 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
16199 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
16200 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
16201 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
16202 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
16203 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
16204 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
16205 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
16206 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
16207 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
16208 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
16209 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
16210 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
16211 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
16212 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
16213 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
16214 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
16215 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
16216 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
16217 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
16218 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
16219 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
16220 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
16221
16222 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
16223 #define kRegistrationTestResultType_Query CFSTR( "query" )
16224 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
16225 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
16226 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
16227
16228 static OSStatus
16229 _RegistrationTestAppendMissingResults(
16230 CFMutableArrayRef inMissingResults,
16231 const RegistrationResultTimes * inTimes,
16232 uint32_t inIfIndex,
16233 const char * inIfName );
16234
16235 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest )
16236 {
16237 OSStatus err;
16238 RegistrationSubtest * subtest;
16239 CFMutableDictionaryRef subtestReport;
16240 CFMutableArrayRef missing;
16241 char * txtStr;
16242 NanoTime64 now;
16243 Boolean subtestFailed;
16244 char startTime[ 32 ];
16245 char endTime[ 32 ];
16246 char ifNameBuf[ IF_NAMESIZE + 1 ];
16247
16248 now = NanoTimeGetCurrent();
16249
16250 subtest = inTest->subtest;
16251 inTest->subtest = NULL;
16252 _RegistrationSubtestStop( subtest );
16253
16254 missing = NULL;
16255 subtestReport = NULL;
16256 txtStr = NULL;
16257 if( subtest->txtPtr )
16258 {
16259 err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, NULL, 0, &txtStr );
16260 require_noerr( err, exit );
16261 }
16262 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16263 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16264 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
16265 "{"
16266 "%kO=%s" // description
16267 "%kO=%s" // startTime
16268 "%kO=%s" // endTime
16269 "%kO=%s" // serviceFQDN
16270 "%kO=%lli" // ifIndex
16271 "%kO=%s" // ifName
16272 "%kO=%lli" // port
16273 "%kO=%s" // txt
16274 "%kO=%b" // registered
16275 "%kO=%b" // usedDefaultName
16276 "%kO=%b" // usedLODiscovery
16277 "}",
16278 kRegistrationTestReportKey_Description, subtest->description,
16279 kRegistrationTestReportKey_StartTime, startTime,
16280 kRegistrationTestReportKey_EndTime, endTime,
16281 kRegistrationTestReportKey_ServiceFQDN, subtest->serviceFQDN,
16282 kRegistrationTestReportKey_InterfaceIndex, (int64_t) subtest->ifIndex,
16283 kRegistrationTestReportKey_InterfaceName, if_indextoname( subtest->ifIndex, ifNameBuf ),
16284 kRegistrationTestReportKey_Port, (int64_t) subtest->port,
16285 kRegistrationTestReportKey_TXT, txtStr,
16286 kRegistrationTestReportKey_Registered, (int) subtest->registered,
16287 kRegistrationTestReportKey_UsedDefaultName, (int) subtest->useDefaultName,
16288 kRegistrationTestReportKey_UsedLODiscovery, (int) subtest->useLODiscovery );
16289 ForgetMem( &txtStr );
16290 require_noerr( err, exit );
16291
16292 if( !subtest->skipped && subtest->registered )
16293 {
16294 missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16295 require_action( missing, exit, err = kNoMemoryErr );
16296
16297 if( subtest->ifList )
16298 {
16299 RegistrationInterfaceItem * item;
16300
16301 for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16302 {
16303 #if( TARGET_OS_WATCH )
16304 if( inTest->forBATS && item->base.isWiFi ) continue;
16305 #endif
16306 err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
16307 require_noerr( err, exit );
16308 }
16309 }
16310 else
16311 {
16312 err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
16313 require_noerr( err, exit );
16314 }
16315
16316 subtestFailed = false;
16317 if( CFArrayGetCount( missing ) > 0 )
16318 {
16319 subtestFailed = true;
16320 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
16321 }
16322 if( CFArrayGetCount( subtest->unexpected ) > 0 )
16323 {
16324 subtestFailed = true;
16325 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
16326 }
16327 #if( TARGET_OS_WATCH )
16328 if( CFArrayGetCount( subtest->ignored ) > 0 )
16329 {
16330 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
16331 }
16332 #endif
16333 }
16334 else
16335 {
16336 subtestFailed = true;
16337 }
16338
16339 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
16340 if( subtestFailed )
16341 {
16342 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
16343 if( !subtest->skipped ) inTest->failed = true;
16344 }
16345 CFArrayAppendValue( inTest->subtestReports, subtestReport );
16346
16347 exit:
16348 CFReleaseNullSafe( missing );
16349 CFReleaseNullSafe( subtestReport );
16350 _RegistrationSubtestFree( subtest );
16351 return( err );
16352 }
16353
16354 static OSStatus
16355 _RegistrationTestAppendMissingResult(
16356 CFMutableArrayRef inMissingResults,
16357 CFStringRef inType,
16358 uint32_t inIfIndex,
16359 const char * inIfName );
16360
16361 static OSStatus
16362 _RegistrationTestAppendMissingResults(
16363 CFMutableArrayRef inMissingResults,
16364 const RegistrationResultTimes * inTimes,
16365 uint32_t inIfIndex,
16366 const char * inIfName )
16367 {
16368 OSStatus err;
16369
16370 if( !inTimes->browseResultTime )
16371 {
16372 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
16373 inIfIndex, inIfName );
16374 require_noerr( err, exit );
16375 }
16376 if( !inTimes->querySRVResultTime )
16377 {
16378 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
16379 inIfIndex, inIfName );
16380 require_noerr( err, exit );
16381 }
16382 if( !inTimes->queryTXTResultTime )
16383 {
16384 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
16385 inIfIndex, inIfName );
16386 require_noerr( err, exit );
16387 }
16388 err = kNoErr;
16389
16390 exit:
16391 return( err );
16392 }
16393
16394 static OSStatus
16395 _RegistrationTestAppendMissingResult(
16396 CFMutableArrayRef inMissingResults,
16397 CFStringRef inType,
16398 uint32_t inIfIndex,
16399 const char * inIfName )
16400 {
16401 OSStatus err;
16402 char ifName[ IF_NAMESIZE + 1 ];
16403
16404 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
16405 "{"
16406 "%kO=%O" // resultType
16407 "%kO=%lli" // ifIndex
16408 "%kO=%s" // ifName
16409 "}",
16410 kRegistrationTestReportKey_ResultType, inType,
16411 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16412 kRegistrationTestReportKey_InterfaceName, inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
16413 return( err );
16414 }
16415
16416 //===========================================================================================================================
16417
16418 static void _RegistrationTestEnd( RegistrationTest *inTest )
16419 {
16420 OSStatus err;
16421 NanoTime64 now;
16422 CFPropertyListRef plist;
16423 char startTime[ 32 ];
16424 char endTime[ 32 ];
16425
16426 now = NanoTimeGetCurrent();
16427 _NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
16428 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16429
16430 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16431 "{"
16432 "%kO=%s" // startTime
16433 "%kO=%s" // endTime
16434 "%kO=%s" // computerName
16435 "%kO=%s" // localHostName
16436 "%kO=%O" // subtests
16437 "%kO=%b" // pass
16438 "}",
16439 kRegistrationTestReportKey_StartTime, startTime,
16440 kRegistrationTestReportKey_EndTime, endTime,
16441 kRegistrationTestReportKey_ComputerName, inTest->computerName,
16442 kRegistrationTestReportKey_LocalHostName, inTest->localHostName,
16443 kRegistrationTestReportKey_Subtests, inTest->subtestReports,
16444 kRegistrationTestReportKey_Pass, inTest->failed ? false : true );
16445 require_noerr( err, exit );
16446
16447 err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
16448 CFRelease( plist );
16449 require_noerr( err, exit );
16450
16451 exit:
16452 _RegistrationTestExit( inTest, err );
16453 }
16454
16455 //===========================================================================================================================
16456
16457 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
16458 {
16459 int exitCode;
16460
16461 if( inError )
16462 {
16463 FPrintF( stderr, "error: %#m\n", inError );
16464 exitCode = 1;
16465 }
16466 else
16467 {
16468 exitCode = inTest->failed ? 2 : 0;
16469 }
16470 _RegistrationTestForget( &inTest );
16471 exit( exitCode );
16472 }
16473
16474 //===========================================================================================================================
16475
16476 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
16477 {
16478 OSStatus err;
16479 RegistrationSubtest * obj;
16480
16481 obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
16482 require_action( obj, exit, err = kNoMemoryErr );
16483
16484 obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16485 require_action( obj->unexpected, exit, err = kNoMemoryErr );
16486
16487 #if( TARGET_OS_WATCH )
16488 obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16489 require_action( obj->ignored, exit, err = kNoMemoryErr );
16490 #endif
16491
16492 *outSubtest = obj;
16493 obj = NULL;
16494 err = kNoErr;
16495
16496 exit:
16497 if( obj ) _RegistrationSubtestFree( obj );
16498 return( err );
16499 }
16500
16501 //===========================================================================================================================
16502
16503 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest )
16504 {
16505 check( !inSubtest->registration );
16506 check( !inSubtest->browse );
16507 check( !inSubtest->querySRV );
16508 check( !inSubtest->queryTXT );
16509 check( !inSubtest->connection );
16510 ForgetMem( &inSubtest->serviceNameCustom );
16511 ForgetMem( &inSubtest->serviceType );
16512 ForgetMem( &inSubtest->serviceFQDN );
16513 ForgetMem( &inSubtest->txtPtr );
16514 ForgetCF( &inSubtest->unexpected );
16515 #if( TARGET_OS_WATCH )
16516 ForgetCF( &inSubtest->ignored );
16517 #endif
16518 _MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
16519 ForgetMem( &inSubtest->description );
16520 free( inSubtest );
16521 }
16522
16523 //===========================================================================================================================
16524
16525 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest )
16526 {
16527 DNSServiceForget( &inSubtest->registration );
16528 DNSServiceForget( &inSubtest->browse );
16529 DNSServiceForget( &inSubtest->querySRV );
16530 DNSServiceForget( &inSubtest->queryTXT );
16531 DNSServiceForget( &inSubtest->connection );
16532 }
16533
16534 //===========================================================================================================================
16535
16536 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
16537 {
16538 OSStatus err;
16539 RegistrationInterfaceItem * list;
16540 const MDNSInterfaceSubset subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
16541
16542 err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
16543 require_noerr( err, exit );
16544
16545 *outList = list;
16546
16547 exit:
16548 return( err );
16549 }
16550
16551 //===========================================================================================================================
16552
16553 static OSStatus
16554 _RegistrationTestCreateRandomTXTRecord(
16555 size_t inMinLen,
16556 size_t inMaxLen,
16557 uint8_t ** outTXTPtr,
16558 size_t * outTXTLen )
16559 {
16560 OSStatus err;
16561 uint8_t * ptr;
16562 const uint8_t * txtEnd;
16563 uint8_t * txtPtr = NULL;
16564 size_t txtLen;
16565
16566 require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
16567
16568 txtLen = RandomRange( inMinLen, inMaxLen );
16569 txtPtr = (uint8_t *) malloc( txtLen + 1 );
16570 require_action( txtPtr, exit, err = kNoMemoryErr );
16571
16572 _RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
16573
16574 ptr = txtPtr;
16575 txtEnd = &txtPtr[ txtLen ];
16576 while( ptr < txtEnd )
16577 {
16578 size_t maxLen, len;
16579
16580 maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
16581 len = RandomRange( 1, 255 );
16582 if( len > maxLen ) len = maxLen;
16583
16584 *ptr = (uint8_t) len;
16585 ptr += ( 1 + len );
16586 }
16587 check( ptr == txtEnd );
16588
16589 if( outTXTPtr )
16590 {
16591 *outTXTPtr = txtPtr;
16592 txtPtr = NULL;
16593 }
16594 if( outTXTLen ) *outTXTLen = txtLen;
16595 err = kNoErr;
16596
16597 exit:
16598 FreeNullSafe( txtPtr );
16599 return( err );
16600 }
16601
16602 //===========================================================================================================================
16603
16604 static void DNSSD_API
16605 _RegistrationSubtestRegisterCallback(
16606 DNSServiceRef inSDRef,
16607 DNSServiceFlags inFlags,
16608 DNSServiceErrorType inError,
16609 const char * inServiceName,
16610 const char * inServiceType,
16611 const char * inDomain,
16612 void * inContext )
16613 {
16614 OSStatus err;
16615 const NanoTime64 now = NanoTimeGetCurrent();
16616 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16617
16618 Unused( inSDRef );
16619
16620 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
16621 ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16622 _RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
16623 ( strcasecmp( inDomain, "local." ) == 0 ) )
16624 {
16625 if( !subtest->registered )
16626 {
16627 DNSServiceRef sdRef;
16628 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16629
16630 subtest->registered = true;
16631
16632 // Create shared connection.
16633
16634 check( !subtest->connection );
16635 err = DNSServiceCreateConnection( &subtest->connection );
16636 require_noerr( err, exit );
16637
16638 err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
16639 require_noerr( err, exit );
16640
16641 // Start browse.
16642
16643 check( !subtest->browse );
16644 sdRef = subtest->connection;
16645 err = DNSServiceBrowse( &sdRef, flags,
16646 subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
16647 subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
16648 require_noerr( err, exit );
16649
16650 subtest->browse = sdRef;
16651 }
16652 }
16653 else
16654 {
16655 char timestamp[ 32 ];
16656
16657 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
16658 "{"
16659 "%kO=%O" // resultType
16660 "%kO=%s" // timestamp
16661 "%kO=%lli" // flags
16662 "%kO=%lli" // error
16663 "%kO=%s" // serviceName
16664 "%kO=%s" // serviceType
16665 "%kO=%s" // domain
16666 "}",
16667 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Registration,
16668 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16669 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16670 kRegistrationTestReportKey_Error, (int64_t) inError,
16671 kRegistrationTestReportKey_ServiceName, inServiceName,
16672 kRegistrationTestReportKey_ServiceType, inServiceType,
16673 kRegistrationTestReportKey_Domain, inDomain );
16674 require_noerr( err, exit );
16675 }
16676 err = kNoErr;
16677
16678 exit:
16679 if( err ) _RegistrationTestExit( subtest->test, err );
16680 }
16681
16682 //===========================================================================================================================
16683
16684 static void DNSSD_API
16685 _RegistrationSubtestBrowseCallback(
16686 DNSServiceRef inSDRef,
16687 DNSServiceFlags inFlags,
16688 uint32_t inIfIndex,
16689 DNSServiceErrorType inError,
16690 const char * inServiceName,
16691 const char * inServiceType,
16692 const char * inDomain,
16693 void * inContext )
16694 {
16695 OSStatus err;
16696 NanoTime64 now;
16697 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16698 Boolean serviceIsCorrect, resultIsExpected;
16699
16700 Unused( inSDRef );
16701
16702 now = NanoTimeGetCurrent();
16703 if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16704 _RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
16705 {
16706 serviceIsCorrect = true;
16707 }
16708 else
16709 {
16710 serviceIsCorrect = false;
16711 }
16712
16713 resultIsExpected = false;
16714 if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
16715 {
16716 RegistrationResultTimes * times;
16717
16718 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
16719 if( times )
16720 {
16721 DNSServiceRef sdRef;
16722 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16723 uint32_t ifIndex;
16724
16725 resultIsExpected = true;
16726 if( !times->browseResultTime ) times->browseResultTime = now;
16727
16728 ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
16729 if( !subtest->querySRV )
16730 {
16731 // Start SRV record query.
16732
16733 sdRef = subtest->connection;
16734 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
16735 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16736 require_noerr( err, exit );
16737
16738 subtest->querySRV = sdRef;
16739 }
16740 if( !subtest->queryTXT )
16741 {
16742 // Start TXT record query.
16743
16744 sdRef = subtest->connection;
16745 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
16746 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16747 require_noerr( err, exit );
16748
16749 subtest->queryTXT = sdRef;
16750 }
16751 }
16752 }
16753
16754 if( !resultIsExpected )
16755 {
16756 CFMutableArrayRef resultArray;
16757 char timestamp[ 32 ];
16758 const char * ifNamePtr;
16759 char ifNameBuf[ IF_NAMESIZE + 1 ];
16760
16761 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16762 resultArray = subtest->unexpected;
16763 #if( TARGET_OS_WATCH )
16764 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16765 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
16766 {
16767 resultArray = subtest->ignored;
16768 }
16769 #endif
16770 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
16771 "{"
16772 "%kO=%O" // resultType
16773 "%kO=%s" // timestamp
16774 "%kO=%lli" // flags
16775 "%kO=%lli" // ifIndex
16776 "%kO=%s" // ifName
16777 "%kO=%lli" // error
16778 "%kO=%s" // serviceName
16779 "%kO=%s" // serviceType
16780 "%kO=%s" // domain
16781 "}",
16782 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Browse,
16783 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16784 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16785 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16786 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16787 kRegistrationTestReportKey_Error, (int64_t) inError,
16788 kRegistrationTestReportKey_ServiceName, inServiceName,
16789 kRegistrationTestReportKey_ServiceType, inServiceType,
16790 kRegistrationTestReportKey_Domain, inDomain );
16791 require_noerr( err, exit );
16792 }
16793 err = kNoErr;
16794
16795 exit:
16796 if( err ) _RegistrationTestExit( subtest->test, err );
16797 }
16798
16799 //===========================================================================================================================
16800
16801 static Boolean
16802 _RegistrationSubtestIsSRVRecordDataValid(
16803 RegistrationSubtest * inSubtest,
16804 const uint8_t * inRDataPtr,
16805 size_t inRDataLen,
16806 Boolean inExpectRandHostname );
16807
16808 static void DNSSD_API
16809 _RegistrationSubtestQueryCallback(
16810 DNSServiceRef inSDRef,
16811 DNSServiceFlags inFlags,
16812 uint32_t inIfIndex,
16813 DNSServiceErrorType inError,
16814 const char * inName,
16815 uint16_t inType,
16816 uint16_t inClass,
16817 uint16_t inRDataLen,
16818 const void * inRDataPtr,
16819 uint32_t inTTL,
16820 void * inContext )
16821 {
16822 OSStatus err;
16823 NanoTime64 now;
16824 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16825 Boolean resultIsExpected;
16826
16827 Unused( inSDRef );
16828 Unused( inTTL );
16829
16830 now = NanoTimeGetCurrent();
16831 resultIsExpected = false;
16832 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
16833 ( inClass == kDNSServiceClass_IN ) )
16834 {
16835 RegistrationResultTimes * times;
16836 Boolean isAWDL;
16837
16838 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
16839 if( times )
16840 {
16841 if( inType == kDNSServiceType_SRV )
16842 {
16843 Boolean expectRandHostname;
16844
16845 if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
16846 {
16847 expectRandHostname = true;
16848 }
16849 else
16850 {
16851 expectRandHostname = false;
16852 }
16853 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16854 {
16855 resultIsExpected = true;
16856 if( !times->querySRVResultTime ) times->querySRVResultTime = now;
16857 }
16858 }
16859 else if( inType == kDNSServiceType_TXT )
16860 {
16861 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16862 {
16863 resultIsExpected = true;
16864 if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
16865 }
16866 }
16867 }
16868 }
16869
16870 if( !resultIsExpected )
16871 {
16872 CFMutableArrayRef resultArray;
16873 CFMutableDictionaryRef resultDict;
16874 CFStringRef rdataKey;
16875 char * rdataStr;
16876 const char * ifNamePtr;
16877 char timestamp[ 32 ];
16878 char ifNameBuf[ IF_NAMESIZE + 1 ];
16879
16880 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16881 resultArray = subtest->unexpected;
16882 #if( TARGET_OS_WATCH )
16883 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16884 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
16885 ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
16886 {
16887 if( inType == kDNSServiceType_SRV )
16888 {
16889 const Boolean expectRandHostname = subtest->includeAWDL ? true : false;
16890
16891 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16892 {
16893 resultArray = subtest->ignored;
16894 }
16895 }
16896 else if( inType == kDNSServiceType_TXT )
16897 {
16898 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16899 {
16900 resultArray = subtest->ignored;
16901 }
16902 }
16903 }
16904 #endif
16905 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
16906 "{"
16907 "%kO=%O" // resultType
16908 "%kO=%s" // timestamp
16909 "%kO=%lli" // flags
16910 "%kO=%lli" // ifIndex
16911 "%kO=%s" // ifName
16912 "%kO=%lli" // error
16913 "%kO=%s" // serviceFQDN
16914 "%kO=%lli" // recordType
16915 "%kO=%lli" // class
16916 "}",
16917 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Query,
16918 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16919 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16920 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16921 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16922 kRegistrationTestReportKey_Error, (int64_t) inError,
16923 kRegistrationTestReportKey_ServiceFQDN, inName,
16924 kRegistrationTestReportKey_RecordType, (int64_t) inType,
16925 kRegistrationTestReportKey_RecordClass, (int64_t) inClass );
16926 require_noerr( err, exit );
16927
16928 rdataStr = NULL;
16929 DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
16930 if( rdataStr )
16931 {
16932 rdataKey = kRegistrationTestReportKey_RDataFormatted;
16933 }
16934 else
16935 {
16936 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16937 require_action( rdataStr, exit, err = kNoMemoryErr );
16938
16939 rdataKey = kRegistrationTestReportKey_RDataHexString;
16940 }
16941 err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
16942 ForgetMem( &rdataStr );
16943 if( err ) CFRelease( resultDict );
16944 require_noerr( err, exit );
16945
16946 CFArrayAppendValue( resultArray, resultDict );
16947 CFRelease( resultDict );
16948 }
16949 err = kNoErr;
16950
16951 exit:
16952 if( err ) _RegistrationTestExit( subtest->test, err );
16953 }
16954
16955 static Boolean
16956 _RegistrationSubtestIsSRVRecordDataValid(
16957 RegistrationSubtest * inSubtest,
16958 const uint8_t * inRDataPtr,
16959 size_t inRDataLen,
16960 Boolean inExpectRandHostname )
16961 {
16962 const dns_fixed_fields_srv * fields;
16963 const uint8_t * const end = &inRDataPtr[ inRDataLen ];
16964 const uint8_t * label;
16965 size_t len;
16966 uint16_t port;
16967 Boolean isValid;
16968
16969 isValid = false;
16970 require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
16971
16972 fields = (const dns_fixed_fields_srv *) inRDataPtr;
16973 port = dns_fixed_fields_srv_get_port( fields );
16974 require_quiet( port == inSubtest->port, exit );
16975
16976 // First target label should be a UUID string for the AWDL interface.
16977
16978 label = (const uint8_t *) &fields[ 1 ];
16979 require_quiet( ( end - label ) >= 1, exit );
16980
16981 len = label[ 0 ];
16982 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16983
16984 if( inExpectRandHostname )
16985 {
16986 if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
16987 }
16988 else
16989 {
16990 if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
16991 }
16992
16993 // Second target label should be "local".
16994
16995 label = &label[ 1 + len ];
16996 require_quiet( ( end - label ) >= 1, exit );
16997
16998 len = label[ 0 ];
16999 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
17000
17001 if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
17002
17003 // Third target label should be the root label.
17004
17005 label = &label[ 1 + len ];
17006 require_quiet( ( end - label ) >= 1, exit );
17007
17008 len = label[ 0 ];
17009 if( len != 0 ) goto exit;
17010
17011 isValid = true;
17012
17013 exit:
17014 return( isValid );
17015 }
17016
17017 //===========================================================================================================================
17018
17019 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
17020 {
17021 if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
17022 {
17023 const char * const ptr = &inServiceType[ inSubtest->serviceTypeLen ];
17024
17025 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
17026 }
17027 return( false );
17028 }
17029
17030 //===========================================================================================================================
17031
17032 static RegistrationResultTimes *
17033 _RegistrationSubtestGetInterfaceResultTimes(
17034 RegistrationSubtest * inSubtest,
17035 uint32_t inIfIndex,
17036 Boolean * outIsAWDL )
17037 {
17038 if( inSubtest->ifList )
17039 {
17040 RegistrationInterfaceItem * item;
17041
17042 for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
17043 {
17044 if( inIfIndex == item->base.ifIndex )
17045 {
17046 if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
17047 return( &item->times );
17048 }
17049 }
17050 }
17051 else
17052 {
17053 if( inIfIndex == inSubtest->ifIndex )
17054 {
17055 if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
17056 return( &inSubtest->ifTimes );
17057 }
17058 }
17059 return( NULL );
17060 }
17061
17062 //===========================================================================================================================
17063
17064 static void _RegistrationTestTimerHandler( void *inContext )
17065 {
17066 RegistrationTest * const test = (RegistrationTest *) inContext;
17067
17068 dispatch_source_forget( &test->timer );
17069 _RegistrationTestProceed( test );
17070 }
17071
17072 //===========================================================================================================================
17073
17074 #if( TARGET_OS_WATCH )
17075 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName )
17076 {
17077 NetTransportType type = kNetTransportType_Undefined;
17078
17079 SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
17080 return( ( type == kNetTransportType_WiFi ) ? true : false );
17081 }
17082 #endif
17083
17084 #if( TARGET_OS_DARWIN )
17085 //===========================================================================================================================
17086 // KeepAliveTestCmd
17087 //===========================================================================================================================
17088
17089 typedef enum
17090 {
17091 kKeepAliveCallVariant_Null = 0,
17092 kKeepAliveCallVariant_TakesSocket = 1, // DNSServiceSleepKeepalive(), which takes a connected socket.
17093 kKeepAliveCallVariant_TakesSockAddrs = 2, // DNSServiceSleepKeepalive_sockaddr(), which takes connection's sockaddrs.
17094
17095 } KeepAliveCallVariant;
17096
17097 typedef struct
17098 {
17099 int family; // TCP connection's address family.
17100 KeepAliveCallVariant callVariant; // Describes which DNSServiceSleepKeepalive* call to use.
17101 const char * description;
17102
17103 } KeepAliveSubtestParams;
17104
17105 const KeepAliveSubtestParams kKeepAliveSubtestParams[] =
17106 {
17107 { AF_INET, kKeepAliveCallVariant_TakesSocket, "Calls DNSServiceSleepKeepalive() for IPv4 TCP connection." },
17108 { AF_INET, kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv4 TCP connection." },
17109 { AF_INET6, kKeepAliveCallVariant_TakesSocket, "Calls DNSServiceSleepKeepalive() for IPv6 TCP connection." },
17110 { AF_INET6, kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv6 TCP connection." }
17111 };
17112
17113 typedef struct
17114 {
17115 sockaddr_ip local; // TCP connection's local address and port.
17116 sockaddr_ip remote; // TCP connection's remote address and port.
17117 NanoTime64 startTime; // Subtest's start time.
17118 NanoTime64 endTime; // Subtest's end time.
17119 SocketRef clientSock; // Socket for client-side of TCP connection.
17120 SocketRef serverSock; // Socket for server-side of TCP connection.
17121 char * recordName; // Keepalive record's name.
17122 char * dataStr; // Data expected to be contained in keepalive record's data.
17123 const char * description; // Subtests's description.
17124 unsigned int timeoutKA; // Randomly-generated timeout value that gets put in keepalive record's rdata.
17125 OSStatus error; // Subtest's error.
17126
17127 } KeepAliveSubtest;
17128
17129 typedef struct KeepAliveTest * KeepAliveTestRef;
17130
17131 typedef struct
17132 {
17133 KeepAliveTestRef test; // Weak back pointer to test.
17134
17135 } KeepAliveTestConnectionContext;
17136
17137 struct KeepAliveTest
17138 {
17139 dispatch_queue_t queue; // Serial queue for test events.
17140 dispatch_semaphore_t doneSem; // Semaphore to signal when the test is done.
17141 dispatch_source_t readSource; // Read source for TCP listener socket.
17142 DNSServiceRef keepalive; // DNSServiceSleepKeepalive{,2} operation.
17143 DNSServiceRef query; // Query to verify registered keepalive record.
17144 dispatch_source_t timer; // Timer to put time limit on query.
17145 AsyncConnectionRef connection; // Establishes current subtest's TCP connection.
17146 KeepAliveTestConnectionContext * connectionCtx; // Weak pointer to connection's context.
17147 NanoTime64 startTime; // Test's start time.
17148 NanoTime64 endTime; // Test's end time.
17149 OSStatus error; // Test's error.
17150 size_t subtestIdx; // Index of current subtest.
17151 KeepAliveSubtest subtests[ 4 ]; // Subtest array.
17152 };
17153 check_compile_time( countof_field( struct KeepAliveTest, subtests ) == countof( kKeepAliveSubtestParams ) );
17154
17155 ulog_define_ex( kDNSSDUtilIdentifier, KeepAliveTest, kLogLevelInfo, kLogFlags_None, "KeepAliveTest", NULL );
17156 #define kat_ulog( LEVEL, ... ) ulog( &log_category_from_name( KeepAliveTest ), (LEVEL), __VA_ARGS__ )
17157
17158 static OSStatus _KeepAliveTestCreate( KeepAliveTestRef *outTest );
17159 static OSStatus _KeepAliveTestRun( KeepAliveTestRef inTest );
17160 static void _KeepAliveTestFree( KeepAliveTestRef inTest );
17161
17162 static void KeepAliveTestCmd( void )
17163 {
17164 OSStatus err;
17165 OutputFormatType outputFormat;
17166 KeepAliveTestRef test = NULL;
17167 CFPropertyListRef plist = NULL;
17168 CFMutableArrayRef subtests;
17169 size_t i;
17170 size_t subtestFailCount;
17171 Boolean testPassed = false;
17172 char startTime[ 32 ];
17173 char endTime[ 32 ];
17174
17175 err = OutputFormatFromArgString( gKeepAliveTest_OutputFormat, &outputFormat );
17176 require_noerr_quiet( err, exit );
17177
17178 err = _KeepAliveTestCreate( &test );
17179 require_noerr( err, exit );
17180
17181 err = _KeepAliveTestRun( test );
17182 require_noerr( err, exit );
17183
17184 _NanoTime64ToTimestamp( test->startTime, startTime, sizeof( startTime ) );
17185 _NanoTime64ToTimestamp( test->endTime, endTime, sizeof( endTime ) );
17186 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
17187 "{"
17188 "%kO=%s" // startTime
17189 "%kO=%s" // endTime
17190 "%kO=[%@]" // subtests
17191 "}",
17192 CFSTR( "startTime" ), startTime,
17193 CFSTR( "endTime" ), endTime,
17194 CFSTR( "subtests" ), &subtests );
17195 require_noerr( err, exit );
17196
17197 subtestFailCount = 0;
17198 check( test->subtestIdx == countof( test->subtests ) );
17199 for( i = 0; i < countof( test->subtests ); ++i )
17200 {
17201 KeepAliveSubtest * const subtest = &test->subtests[ i ];
17202 char errorDesc[ 128 ];
17203
17204 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
17205 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
17206 SNPrintF( errorDesc, sizeof( errorDesc ), "%m", subtest->error );
17207 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtests,
17208 "{"
17209 "%kO=%s" // startTime
17210 "%kO=%s" // endTime
17211 "%kO=%s" // description
17212 "%kO=%##a" // localAddr
17213 "%kO=%##a" // remoteAddr
17214 "%kO=%s" // recordName
17215 "%kO=%s" // expectedRData
17216 "%kO=" // error
17217 "{"
17218 "%kO=%lli" // code
17219 "%kO=%s" // description
17220 "}"
17221 "}",
17222 CFSTR( "startTime" ), startTime,
17223 CFSTR( "endTime" ), endTime,
17224 CFSTR( "description" ), subtest->description,
17225 CFSTR( "localAddr" ), &subtest->local.sa,
17226 CFSTR( "remoteAddr" ), &subtest->remote.sa,
17227 CFSTR( "recordName" ), subtest->recordName,
17228 CFSTR( "expectedRData" ), subtest->dataStr,
17229 CFSTR( "error" ),
17230 CFSTR( "code" ), (int64_t) subtest->error,
17231 CFSTR( "description" ), errorDesc
17232 );
17233 require_noerr( err, exit );
17234 if( subtest->error ) ++subtestFailCount;
17235 }
17236 if( subtestFailCount == 0 ) testPassed = true;
17237 CFPropertyListAppendFormatted( kCFAllocatorDefault, plist, "%kO=%b", CFSTR( "pass" ), testPassed );
17238
17239 err = OutputPropertyList( plist, outputFormat, gKeepAliveTest_OutputFilePath );
17240 require_noerr( err, exit );
17241
17242 exit:
17243 if( test ) _KeepAliveTestFree( test );
17244 CFReleaseNullSafe( plist );
17245 gExitCode = err ? 1 : ( testPassed ? 0 : 2 );
17246 }
17247
17248 //===========================================================================================================================
17249
17250 static void _KeepAliveTestStart( void *inCtx );
17251 static void _KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError );
17252 static OSStatus _KeepAliveTestStartSubtest( KeepAliveTestRef inTest );
17253 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest );
17254 static KeepAliveSubtest * _KeepAliveTestGetSubtest( KeepAliveTestRef inTest );
17255 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen );
17256 static OSStatus _KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone );
17257 static void _KeepAliveTestTCPAcceptHandler( void *inCtx );
17258 static void _KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
17259 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError );
17260 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest );
17261 static void DNSSD_API _KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inErr, void *inCtx );
17262 static void _KeepAliveTestQueryTimerHandler( void *inCtx );
17263 static void DNSSD_API
17264 _KeepAliveTestQueryRecordCallback(
17265 DNSServiceRef inSDRef,
17266 DNSServiceFlags inFlags,
17267 uint32_t inInterfaceIndex,
17268 DNSServiceErrorType inError,
17269 const char * inFullName,
17270 uint16_t inType,
17271 uint16_t inClass,
17272 uint16_t inRDataLen,
17273 const void * inRDataPtr,
17274 uint32_t inTTL,
17275 void * inCtx );
17276
17277 static OSStatus _KeepAliveTestCreate( KeepAliveTestRef *outTest )
17278 {
17279 OSStatus err;
17280 KeepAliveTestRef test;
17281 size_t i;
17282
17283 test = (KeepAliveTestRef) calloc( 1, sizeof( *test ) );
17284 require_action( test, exit, err = kNoMemoryErr );
17285
17286 test->error = kInProgressErr;
17287 for( i = 0; i < countof( test->subtests ); ++i )
17288 {
17289 KeepAliveSubtest * const subtest = &test->subtests[ i ];
17290
17291 subtest->local.sa.sa_family = AF_UNSPEC;
17292 subtest->remote.sa.sa_family = AF_UNSPEC;
17293 subtest->clientSock = kInvalidSocketRef;
17294 subtest->serverSock = kInvalidSocketRef;
17295 }
17296 test->queue = dispatch_queue_create( "com.apple.dnssdutil.keepalive-test", DISPATCH_QUEUE_SERIAL );
17297 require_action( test->queue, exit, err = kNoResourcesErr );
17298
17299 test->doneSem = dispatch_semaphore_create( 0 );
17300 require_action( test->doneSem, exit, err = kNoResourcesErr );
17301
17302 *outTest = test;
17303 test = NULL;
17304 err = kNoErr;
17305
17306 exit:
17307 if( test ) _KeepAliveTestFree( test );
17308 return( err );
17309 }
17310
17311 //===========================================================================================================================
17312
17313 static OSStatus _KeepAliveTestRun( KeepAliveTestRef inTest )
17314 {
17315 dispatch_async_f( inTest->queue, inTest, _KeepAliveTestStart );
17316 dispatch_semaphore_wait( inTest->doneSem, DISPATCH_TIME_FOREVER );
17317 return( inTest->error );
17318 }
17319
17320 //===========================================================================================================================
17321
17322 static void _KeepAliveTestFree( KeepAliveTestRef inTest )
17323 {
17324 size_t i;
17325
17326 check( !inTest->readSource );
17327 check( !inTest->query );
17328 check( !inTest->timer );
17329 check( !inTest->keepalive );
17330 check( !inTest->connection );
17331 check( !inTest->connectionCtx );
17332 dispatch_forget( &inTest->queue );
17333 dispatch_forget( &inTest->doneSem );
17334 for( i = 0; i < countof( inTest->subtests ); ++i )
17335 {
17336 KeepAliveSubtest * const subtest = &inTest->subtests[ i ];
17337
17338 check( !IsValidSocket( subtest->clientSock ) );
17339 check( !IsValidSocket( subtest->serverSock ) );
17340 ForgetMem( &subtest->recordName );
17341 ForgetMem( &subtest->dataStr );
17342 }
17343 free( inTest );
17344 }
17345
17346 //===========================================================================================================================
17347
17348 static void _KeepAliveTestStart( void *inCtx )
17349 {
17350 OSStatus err;
17351 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
17352
17353 test->error = kInProgressErr;
17354 test->startTime = NanoTimeGetCurrent();
17355 err = _KeepAliveTestStartSubtest( test );
17356 require_noerr( err, exit );
17357
17358 exit:
17359 if( err ) _KeepAliveTestStop( test, err );
17360 }
17361
17362 //===========================================================================================================================
17363
17364 static void _KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError )
17365 {
17366 size_t i;
17367
17368 inTest->error = inError;
17369 inTest->endTime = NanoTimeGetCurrent();
17370 _KeepAliveTestStopSubtest( inTest );
17371 for( i = 0; i < countof( inTest->subtests ); ++i )
17372 {
17373 KeepAliveSubtest * const subtest = &inTest->subtests[ i ];
17374
17375 ForgetSocket( &subtest->clientSock );
17376 ForgetSocket( &subtest->serverSock );
17377 }
17378 dispatch_semaphore_signal( inTest->doneSem );
17379 }
17380
17381 //===========================================================================================================================
17382
17383 static OSStatus _KeepAliveTestStartSubtest( KeepAliveTestRef inTest )
17384 {
17385 OSStatus err;
17386 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( inTest );
17387 const KeepAliveSubtestParams * const params = &kKeepAliveSubtestParams[ inTest->subtestIdx ];
17388 int port;
17389 SocketRef sock = kInvalidSocketRef;
17390 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
17391 SocketContext * sockCtx = NULL;
17392 KeepAliveTestConnectionContext * cnxCtx = NULL;
17393 Boolean useIPv4;
17394 char serverAddrStr[ 64 ];
17395 char prefix[ 64 ];
17396
17397 subtest->error = kInProgressErr;
17398 subtest->startTime = NanoTimeGetCurrent();
17399 subtest->description = params->description;
17400
17401 require_action( ( params->family == AF_INET ) || ( params->family == AF_INET6 ), exit, err = kInternalErr );
17402
17403 // Create TCP listener socket.
17404
17405 useIPv4 = ( params->family == AF_INET ) ? true : false;
17406 err = ServerSocketOpenEx( params->family, SOCK_STREAM, IPPROTO_TCP,
17407 useIPv4 ? ( (const void *) &loopbackV4 ) : ( (const void *) &in6addr_loopback ), kSocketPort_Auto, &port,
17408 kSocketBufferSize_DontSet, &sock );
17409 require_noerr( err, exit );
17410
17411 if( useIPv4 ) SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "%.4a:%d", &loopbackV4, port );
17412 else SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "[%.16a]:%d", in6addr_loopback.s6_addr, port );
17413 _KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
17414 kat_ulog( kLogLevelInfo, "%s: Will listen for connections on %s\n", prefix, serverAddrStr );
17415
17416 err = SocketContextCreate( sock, inTest, &sockCtx );
17417 require_noerr( err, exit );
17418 sock = kInvalidSocketRef;
17419
17420 // Create read source for TCP listener socket.
17421
17422 check( !inTest->readSource );
17423 err = DispatchReadSourceCreate( sockCtx->sock, inTest->queue, _KeepAliveTestTCPAcceptHandler,
17424 SocketContextCancelHandler, sockCtx, &inTest->readSource );
17425 require_noerr( err, exit );
17426 sockCtx = NULL;
17427 dispatch_resume( inTest->readSource );
17428
17429 cnxCtx = (KeepAliveTestConnectionContext *) calloc( 1, sizeof( *cnxCtx ) );
17430 require_action( cnxCtx, exit, err = kNoMemoryErr );
17431
17432 // Start asynchronous connection to listener socket.
17433
17434 kat_ulog( kLogLevelInfo, "%s: Will connect to %s\n", prefix, serverAddrStr );
17435
17436 check( !inTest->connection );
17437 err = AsyncConnection_Connect( &inTest->connection, serverAddrStr, 0, kAsyncConnectionFlags_None,
17438 5 * UINT64_C_safe( kNanosecondsPerSecond ), kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
17439 NULL, NULL, _KeepAliveTestConnectionHandler, cnxCtx, inTest->queue );
17440 require_noerr( err, exit );
17441
17442 cnxCtx->test = inTest;
17443 check( !inTest->connectionCtx );
17444 inTest->connectionCtx = cnxCtx;
17445 cnxCtx = NULL;
17446
17447 exit:
17448 ForgetSocket( &sock );
17449 if( sockCtx ) SocketContextRelease( sockCtx );
17450 FreeNullSafe( cnxCtx );
17451 return( err );
17452 }
17453
17454 //===========================================================================================================================
17455
17456 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest )
17457 {
17458 dispatch_source_forget( &inTest->readSource );
17459 DNSServiceForget( &inTest->keepalive );
17460 DNSServiceForget( &inTest->query );
17461 dispatch_source_forget( &inTest->timer );
17462 _KeepAliveTestForgetConnection( inTest );
17463 }
17464
17465 //===========================================================================================================================
17466
17467 static KeepAliveSubtest * _KeepAliveTestGetSubtest( KeepAliveTestRef inTest )
17468 {
17469 return( ( inTest->subtestIdx < countof( inTest->subtests ) ) ? &inTest->subtests[ inTest->subtestIdx ] : NULL );
17470 }
17471
17472 //===========================================================================================================================
17473
17474 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen )
17475 {
17476 SNPrintF( inBufPtr, inBufLen, "Subtest %zu/%zu", inTest->subtestIdx + 1, countof( inTest->subtests ) );
17477 return( inBufPtr );
17478 }
17479
17480 //===========================================================================================================================
17481
17482 static OSStatus _KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone )
17483 {
17484 OSStatus err;
17485 KeepAliveSubtest * subtest;
17486
17487 require_action( inTest->subtestIdx <= countof( inTest->subtests ), exit, err = kInternalErr );
17488
17489 if( inTest->subtestIdx < countof( inTest->subtests ) )
17490 {
17491 subtest = _KeepAliveTestGetSubtest( inTest );
17492 _KeepAliveTestStopSubtest( inTest );
17493 subtest->endTime = NanoTimeGetCurrent();
17494 subtest->error = inSubtestError;
17495 if( ++inTest->subtestIdx < countof( inTest->subtests ) )
17496 {
17497 err = _KeepAliveTestStartSubtest( inTest );
17498 require_noerr_quiet( err, exit );
17499 }
17500 }
17501 err = kNoErr;
17502
17503 exit:
17504 if( outDone ) *outDone = ( !err && ( inTest->subtestIdx == countof( inTest->subtests ) ) ) ? true : false;
17505 return( err );
17506 }
17507
17508 //===========================================================================================================================
17509
17510 static void _KeepAliveTestTCPAcceptHandler( void *inCtx )
17511 {
17512 OSStatus err;
17513 const SocketContext * const sockCtx = (SocketContext *) inCtx;
17514 const KeepAliveTestRef test = (KeepAliveTestRef) sockCtx->userContext;
17515 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
17516 sockaddr_ip peer;
17517 socklen_t len;
17518 char prefix[ 64 ];
17519
17520 check( !IsValidSocket( subtest->serverSock ) );
17521 len = (socklen_t) sizeof( peer );
17522 subtest->serverSock = accept( sockCtx->sock, &peer.sa, &len );
17523 err = map_socket_creation_errno( subtest->serverSock );
17524 require_noerr( err, exit );
17525
17526 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
17527 kat_ulog( kLogLevelInfo, "%s: Accepted connection from %##a\n", prefix, &peer.sa );
17528
17529 dispatch_source_forget( &test->readSource );
17530
17531 exit:
17532 if( err ) _KeepAliveTestStop( test, err );
17533 }
17534
17535 //===========================================================================================================================
17536
17537 static void _KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
17538 {
17539 KeepAliveTestConnectionContext * ctx = (KeepAliveTestConnectionContext *) inArg;
17540 const KeepAliveTestRef test = ctx->test;
17541
17542 if( test )
17543 {
17544 _KeepAliveTestForgetConnection( test );
17545 _KeepAliveTestHandleConnection( test, inSock, inError );
17546 inSock = kInvalidSocketRef;
17547 }
17548 ForgetSocket( &inSock );
17549 free( ctx );
17550 }
17551
17552 //===========================================================================================================================
17553
17554 #define kKeepAliveTestQueryTimeoutSecs 5
17555
17556 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError )
17557 {
17558 OSStatus err;
17559 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( inTest );
17560 const KeepAliveSubtestParams * const params = &kKeepAliveSubtestParams[ inTest->subtestIdx ];
17561 socklen_t len;
17562 uint32_t value;
17563 int family, i;
17564 Boolean subtestFailed = false;
17565 Boolean done;
17566 char prefix[ 64 ];
17567
17568 require_noerr_action( inError, exit, err = inError );
17569
17570 check( !IsValidSocket( subtest->clientSock ) );
17571 subtest->clientSock = inSock;
17572 inSock = kInvalidSocketRef;
17573
17574 // Get local and remote IP addresses.
17575
17576 len = (socklen_t) sizeof( subtest->local );
17577 err = getsockname( subtest->clientSock, &subtest->local.sa, &len );
17578 err = map_global_noerr_errno( err );
17579 require_noerr( err, exit );
17580
17581 len = (socklen_t) sizeof( subtest->remote );
17582 err = getpeername( subtest->clientSock, &subtest->remote.sa, &len );
17583 err = map_global_noerr_errno( err );
17584 require_noerr( err, exit );
17585
17586 _KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
17587 kat_ulog( kLogLevelInfo, "%s: Connection established: %##a <-> %##a\n",
17588 prefix, &subtest->local.sa, &subtest->remote.sa );
17589
17590 // Call either DNSServiceSleepKeepalive() or DNSServiceSleepKeepalive_sockaddr().
17591
17592 check( subtest->timeoutKA == 0 );
17593 subtest->timeoutKA = (unsigned int) RandomRange( 1, UINT_MAX );
17594
17595 switch( params->callVariant )
17596 {
17597 case kKeepAliveCallVariant_TakesSocket:
17598 kat_ulog( kLogLevelInfo, "%s: Will call DNSServiceSleepKeepalive() for client-side socket\n", prefix );
17599 check( !inTest->keepalive );
17600 err = DNSServiceSleepKeepalive( &inTest->keepalive, 0, subtest->clientSock,
17601 subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
17602 require_noerr( err, exit );
17603
17604 err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
17605 require_noerr( err, exit );
17606 break;
17607
17608 case kKeepAliveCallVariant_TakesSockAddrs:
17609 kat_ulog( kLogLevelInfo,
17610 "%s: Will call DNSServiceSleepKeepalive_sockaddr() for local and remote sockaddrs\n", prefix );
17611 if( !SOFT_LINK_HAS_FUNCTION( system_dnssd, DNSServiceSleepKeepalive_sockaddr ) )
17612 {
17613 kat_ulog( kLogLevelError,
17614 "%s: Failed to soft link DNSServiceSleepKeepalive_sockaddr from libsystem_dnssd.\n", prefix );
17615 subtestFailed = true;
17616 err = kUnsupportedErr;
17617 goto exit;
17618 }
17619 check( !inTest->keepalive );
17620 err = soft_DNSServiceSleepKeepalive_sockaddr( &inTest->keepalive, 0, &subtest->local.sa, &subtest->remote.sa,
17621 subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
17622 require_noerr( err, exit );
17623
17624 err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
17625 require_noerr( err, exit );
17626 break;
17627
17628 default:
17629 kat_ulog( kLogLevelError, "%s: Invalid KeepAliveCallVariant value %d\n", prefix, (int) params->callVariant );
17630 err = kInternalErr;
17631 goto exit;
17632 }
17633 // Use the same logic that the DNSServiceSleepKeepalive functions use to derive a record name and rdata.
17634
17635 value = 0;
17636 family = subtest->local.sa.sa_family;
17637 if( family == AF_INET )
17638 {
17639 const struct sockaddr_in * const sin = &subtest->local.v4;
17640 const uint8_t * ptr;
17641
17642 check_compile_time_code( sizeof( sin->sin_addr.s_addr ) == 4 );
17643 ptr = (const uint8_t *) &sin->sin_addr.s_addr;
17644 for( i = 0; i < 4; ++i ) value += ptr[ i ];
17645 value += sin->sin_port; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
17646
17647 check( subtest->remote.sa.sa_family == AF_INET );
17648 ASPrintF( &subtest->dataStr, "t=%u h=%.4a d=%.4a l=%u r=%u",
17649 subtest->timeoutKA, &subtest->local.v4.sin_addr.s_addr, &subtest->remote.v4.sin_addr.s_addr,
17650 ntohs( subtest->local.v4.sin_port ), ntohs( subtest->remote.v4.sin_port ) );
17651 require_action( subtest->dataStr, exit, err = kNoMemoryErr );
17652 }
17653 else if( family == AF_INET6 )
17654 {
17655 const struct sockaddr_in6 * const sin6 = &subtest->local.v6;
17656
17657 check_compile_time_code( countof( sin6->sin6_addr.s6_addr ) == 16 );
17658 for( i = 0; i < 16; ++i ) value += sin6->sin6_addr.s6_addr[ i ];
17659 value += sin6->sin6_port; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
17660
17661 check( subtest->remote.sa.sa_family == AF_INET6 );
17662 ASPrintF( &subtest->dataStr, "t=%u H=%.16a D=%.16a l=%u r=%u",
17663 subtest->timeoutKA, subtest->local.v6.sin6_addr.s6_addr, subtest->remote.v6.sin6_addr.s6_addr,
17664 ntohs( subtest->local.v6.sin6_port ), ntohs( subtest->remote.v6.sin6_port ) );
17665 require_action( subtest->dataStr, exit, err = kNoMemoryErr );
17666 }
17667 else
17668 {
17669 kat_ulog( kLogLevelError, "%s: Unexpected local address family %d\n", prefix, family );
17670 err = kInternalErr;
17671 goto exit;
17672 }
17673
17674 // Start query for the new keepalive record.
17675
17676 check( !subtest->recordName );
17677 ASPrintF( &subtest->recordName, "%u._keepalive._dns-sd._udp.local.", value );
17678 require_action( subtest->recordName, exit, err = kNoMemoryErr );
17679
17680 kat_ulog( kLogLevelInfo, "%s: Will query for %s NULL record\n", prefix, subtest->recordName );
17681 check( !inTest->query );
17682 err = DNSServiceQueryRecord( &inTest->query, 0, kDNSServiceInterfaceIndexLocalOnly, subtest->recordName,
17683 kDNSServiceType_NULL, kDNSServiceClass_IN, _KeepAliveTestQueryRecordCallback, inTest );
17684 require_noerr( err, exit );
17685
17686 err = DNSServiceSetDispatchQueue( inTest->query, inTest->queue );
17687 require_noerr( err, exit );
17688
17689 // Start timer to enforce a time limit on the query.
17690
17691 check( !inTest->timer );
17692 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kKeepAliveTestQueryTimeoutSecs ),
17693 kKeepAliveTestQueryTimeoutSecs * ( INT64_C_safe( kNanosecondsPerSecond ) / 20 ), inTest->queue,
17694 _KeepAliveTestQueryTimerHandler, inTest, &inTest->timer );
17695 require_noerr( err, exit );
17696 dispatch_resume( inTest->timer );
17697
17698 exit:
17699 ForgetSocket( &inSock );
17700 if( subtestFailed )
17701 {
17702 err = _KeepAliveTestContinue( inTest, err, &done );
17703 check_noerr( err );
17704 }
17705 else
17706 {
17707 done = false;
17708 }
17709 if( err || done ) _KeepAliveTestStop( inTest, err );
17710 }
17711
17712 //===========================================================================================================================
17713
17714 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest )
17715 {
17716 if( inTest->connection )
17717 {
17718 check( inTest->connectionCtx );
17719 inTest->connectionCtx->test = NULL; // Unset the connection's back pointer to test.
17720 inTest->connectionCtx = NULL; // Context will be freed by the connection's handler.
17721 AsyncConnection_Forget( &inTest->connection );
17722 }
17723 }
17724
17725 //===========================================================================================================================
17726
17727 static void DNSSD_API _KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
17728 {
17729 OSStatus err;
17730 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
17731 char prefix[ 64 ];
17732
17733 Unused( inSDRef );
17734
17735 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
17736 kat_ulog( kLogLevelInfo, "%s: Keepalive callback error: %#m\n", prefix, inError );
17737
17738 if( inError )
17739 {
17740 Boolean done;
17741
17742 err = _KeepAliveTestContinue( test, inError, &done );
17743 check_noerr( err );
17744 if( err || done ) _KeepAliveTestStop( test, err );
17745 }
17746 }
17747
17748 //===========================================================================================================================
17749
17750 static void _KeepAliveTestQueryTimerHandler( void *inCtx )
17751 {
17752 OSStatus err;
17753 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
17754 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
17755 Boolean done;
17756 char prefix[ 64 ];
17757
17758 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
17759 kat_ulog( kLogLevelInfo, "%s: Query for \"%s\" timed out.\n", prefix, subtest->recordName );
17760
17761 err = _KeepAliveTestContinue( test, kTimeoutErr, &done );
17762 check_noerr( err );
17763 if( err || done ) _KeepAliveTestStop( test, err );
17764 }
17765
17766 //===========================================================================================================================
17767
17768 static void DNSSD_API
17769 _KeepAliveTestQueryRecordCallback(
17770 DNSServiceRef inSDRef,
17771 DNSServiceFlags inFlags,
17772 uint32_t inInterfaceIndex,
17773 DNSServiceErrorType inError,
17774 const char * inFullName,
17775 uint16_t inType,
17776 uint16_t inClass,
17777 uint16_t inRDataLen,
17778 const void * inRDataPtr,
17779 uint32_t inTTL,
17780 void * inCtx )
17781 {
17782 OSStatus err;
17783 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
17784 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
17785 const uint8_t * ptr;
17786 size_t dataStrLen, minLen;
17787 Boolean done;
17788 char prefix[ 64 ];
17789
17790 Unused( inSDRef );
17791 Unused( inInterfaceIndex );
17792 Unused( inTTL );
17793
17794 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
17795 if( strcasecmp( inFullName, subtest->recordName ) != 0 )
17796 {
17797 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record name \"%s\".\n",
17798 prefix, subtest->recordName, inFullName );
17799 err = kUnexpectedErr;
17800 goto exit;
17801 }
17802 if( inType != kDNSServiceType_NULL )
17803 {
17804 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record type %d (%s) != %d (NULL).\n",
17805 prefix, subtest->recordName, inType, RecordTypeToString( inType ), kDNSServiceType_NULL );
17806 err = kUnexpectedErr;
17807 goto exit;
17808 }
17809 if( inClass != kDNSServiceClass_IN )
17810 {
17811 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record class %d != %d (IN).\n",
17812 prefix, subtest->recordName, inClass, kDNSServiceClass_IN );
17813 err = kUnexpectedErr;
17814 goto exit;
17815 }
17816 if( inError )
17817 {
17818 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected error %#m.\n",
17819 prefix, subtest->recordName, inError );
17820 err = inError;
17821 goto exit;
17822 }
17823 if( ( inFlags & kDNSServiceFlagsAdd ) == 0 )
17824 {
17825 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Missing Add flag.\n", prefix, subtest->recordName );
17826 err = kUnexpectedErr;
17827 goto exit;
17828 }
17829 kat_ulog( kLogLevelInfo, "%s: QueryRecord(%s) result rdata: %#H\n",
17830 prefix, subtest->recordName, inRDataPtr, inRDataLen, inRDataLen );
17831
17832 dataStrLen = strlen( subtest->dataStr ) + 1; // There's a NUL terminator at the end of the rdata.
17833 minLen = 1 + dataStrLen; // The first byte of the rdata is a length byte.
17834 if( inRDataLen < minLen )
17835 {
17836 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata length (%d) is less than expected minimum (%zu).\n",
17837 prefix, subtest->recordName, inRDataLen, minLen );
17838 err = kUnexpectedErr;
17839 goto exit;
17840 }
17841 ptr = (const uint8_t *) inRDataPtr;
17842 if( ptr[ 0 ] < dataStrLen )
17843 {
17844 kat_ulog( kLogLevelError,
17845 "%s: QueryRecord(%s) result: rdata length byte value (%d) is less than expected minimum (%zu).\n",
17846 prefix, subtest->recordName, ptr[ 0 ], dataStrLen );
17847 err = kUnexpectedErr;
17848 goto exit;
17849 }
17850 if( memcmp( &ptr[ 1 ], subtest->dataStr, dataStrLen - 1 ) != 0 )
17851 {
17852 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata body doesn't contain '%s'.\n",
17853 prefix, subtest->recordName, subtest->dataStr );
17854 }
17855 err = kNoErr;
17856
17857 exit:
17858 err = _KeepAliveTestContinue( test, err, &done );
17859 check_noerr( err );
17860 if( err || done ) _KeepAliveTestStop( test, kNoErr );
17861 }
17862 #endif // TARGET_OS_DARWIN
17863
17864 //===========================================================================================================================
17865 // SSDPDiscoverCmd
17866 //===========================================================================================================================
17867
17868 #define kSSDPPort 1900
17869
17870 typedef struct
17871 {
17872 HTTPHeader header; // HTTP header object for sending and receiving.
17873 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
17874 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
17875 int receiveSecs; // After send, the amount of time to spend receiving.
17876 uint32_t ifindex; // Index of the interface over which to send the query.
17877 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
17878 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
17879
17880 } SSDPDiscoverContext;
17881
17882 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
17883 static void SSDPDiscoverReadHandler( void *inContext );
17884 static int SocketToPortNumber( SocketRef inSock );
17885 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
17886
17887 static void SSDPDiscoverCmd( void )
17888 {
17889 OSStatus err;
17890 struct timeval now;
17891 SSDPDiscoverContext * context;
17892 dispatch_source_t signalSource = NULL;
17893 SocketRef sockV4 = kInvalidSocketRef;
17894 SocketRef sockV6 = kInvalidSocketRef;
17895 ssize_t n;
17896 int sendCount;
17897
17898 // Set up SIGINT handler.
17899
17900 signal( SIGINT, SIG_IGN );
17901 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
17902 require_noerr( err, exit );
17903 dispatch_resume( signalSource );
17904
17905 // Check command parameters.
17906
17907 if( gSSDPDiscover_ReceiveSecs < -1 )
17908 {
17909 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
17910 err = kParamErr;
17911 goto exit;
17912 }
17913
17914 // Create context.
17915
17916 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
17917 require_action( context, exit, err = kNoMemoryErr );
17918
17919 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
17920 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
17921 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
17922
17923 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
17924 require_noerr_quiet( err, exit );
17925
17926 // Set up IPv4 socket.
17927
17928 if( context->useIPv4 )
17929 {
17930 int port;
17931 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
17932 require_noerr( err, exit );
17933
17934 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
17935 require_noerr( err, exit );
17936
17937 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
17938 err = map_socket_noerr_errno( sockV4, err );
17939 require_noerr( err, exit );
17940 }
17941
17942 // Set up IPv6 socket.
17943
17944 if( context->useIPv6 )
17945 {
17946 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
17947 require_noerr( err, exit );
17948
17949 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
17950 require_noerr( err, exit );
17951
17952 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
17953 err = map_socket_noerr_errno( sockV6, err );
17954 require_noerr( err, exit );
17955 }
17956
17957 // Print prologue.
17958
17959 SSDPDiscoverPrintPrologue( context );
17960
17961 // Send mDNS query message.
17962
17963 sendCount = 0;
17964 if( IsValidSocket( sockV4 ) )
17965 {
17966 struct sockaddr_in mcastAddr4;
17967
17968 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
17969 SIN_LEN_SET( &mcastAddr4 );
17970 mcastAddr4.sin_family = AF_INET;
17971 mcastAddr4.sin_port = htons( kSSDPPort );
17972 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
17973
17974 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
17975 require_noerr( err, exit );
17976
17977 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
17978 (socklen_t) sizeof( mcastAddr4 ) );
17979 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
17980 if( err )
17981 {
17982 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
17983 ForgetSocket( &sockV4 );
17984 }
17985 else
17986 {
17987 if( gSSDPDiscover_Verbose )
17988 {
17989 gettimeofday( &now, NULL );
17990 FPrintF( stdout, "---\n" );
17991 FPrintF( stdout, "Send time: %{du:time}\n", &now );
17992 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
17993 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
17994 FPrintF( stdout, "Message size: %zu\n", context->header.len );
17995 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
17996 }
17997 ++sendCount;
17998 }
17999 }
18000
18001 if( IsValidSocket( sockV6 ) )
18002 {
18003 struct sockaddr_in6 mcastAddr6;
18004
18005 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
18006 SIN6_LEN_SET( &mcastAddr6 );
18007 mcastAddr6.sin6_family = AF_INET6;
18008 mcastAddr6.sin6_port = htons( kSSDPPort );
18009 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
18010 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
18011 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
18012
18013 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
18014 require_noerr( err, exit );
18015
18016 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
18017 (socklen_t) sizeof( mcastAddr6 ) );
18018 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
18019 if( err )
18020 {
18021 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
18022 ForgetSocket( &sockV6 );
18023 }
18024 else
18025 {
18026 if( gSSDPDiscover_Verbose )
18027 {
18028 gettimeofday( &now, NULL );
18029 FPrintF( stdout, "---\n" );
18030 FPrintF( stdout, "Send time: %{du:time}\n", &now );
18031 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
18032 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
18033 FPrintF( stdout, "Message size: %zu\n", context->header.len );
18034 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
18035 }
18036 ++sendCount;
18037 }
18038 }
18039 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
18040
18041 // If there's no wait period after the send, then exit.
18042
18043 if( context->receiveSecs == 0 ) goto exit;
18044
18045 // Create dispatch read sources for socket(s).
18046
18047 if( IsValidSocket( sockV4 ) )
18048 {
18049 SocketContext * sockCtx;
18050
18051 err = SocketContextCreate( sockV4, context, &sockCtx );
18052 require_noerr( err, exit );
18053 sockV4 = kInvalidSocketRef;
18054
18055 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
18056 &context->readSourceV4 );
18057 if( err ) ForgetSocketContext( &sockCtx );
18058 require_noerr( err, exit );
18059
18060 dispatch_resume( context->readSourceV4 );
18061 }
18062
18063 if( IsValidSocket( sockV6 ) )
18064 {
18065 SocketContext * sockCtx;
18066
18067 err = SocketContextCreate( sockV6, context, &sockCtx );
18068 require_noerr( err, exit );
18069 sockV6 = kInvalidSocketRef;
18070
18071 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
18072 &context->readSourceV6 );
18073 if( err ) ForgetSocketContext( &sockCtx );
18074 require_noerr( err, exit );
18075
18076 dispatch_resume( context->readSourceV6 );
18077 }
18078
18079 if( context->receiveSecs > 0 )
18080 {
18081 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
18082 Exit );
18083 }
18084 dispatch_main();
18085
18086 exit:
18087 ForgetSocket( &sockV4 );
18088 ForgetSocket( &sockV6 );
18089 dispatch_source_forget( &signalSource );
18090 exit( err ? 1 : 0 );
18091 }
18092
18093 static int SocketToPortNumber( SocketRef inSock )
18094 {
18095 OSStatus err;
18096 sockaddr_ip sip;
18097 socklen_t len;
18098
18099 len = (socklen_t) sizeof( sip );
18100 err = getsockname( inSock, &sip.sa, &len );
18101 err = map_socket_noerr_errno( inSock, err );
18102 check_noerr( err );
18103 return( err ? -1 : SockAddrGetPort( &sip ) );
18104 }
18105
18106 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
18107 {
18108 OSStatus err;
18109
18110 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
18111 require_noerr( err, exit );
18112
18113 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
18114 require_noerr( err, exit );
18115
18116 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
18117 require_noerr( err, exit );
18118
18119 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
18120 require_noerr( err, exit );
18121
18122 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
18123 require_noerr( err, exit );
18124
18125 err = HTTPHeader_Commit( inHeader );
18126 require_noerr( err, exit );
18127
18128 exit:
18129 return( err );
18130 }
18131
18132 //===========================================================================================================================
18133 // SSDPDiscoverPrintPrologue
18134 //===========================================================================================================================
18135
18136 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
18137 {
18138 const int receiveSecs = inContext->receiveSecs;
18139 const char * ifName;
18140 char ifNameBuf[ IF_NAMESIZE + 1 ];
18141 NetTransportType ifType;
18142
18143 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
18144
18145 ifType = kNetTransportType_Undefined;
18146 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
18147
18148 FPrintF( stdout, "Interface: %s/%d/%s\n",
18149 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
18150 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
18151 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
18152 FPrintF( stdout, "Receive duration: " );
18153 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
18154 else FPrintF( stdout, "∞\n" );
18155 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18156 }
18157
18158 //===========================================================================================================================
18159 // SSDPDiscoverReadHandler
18160 //===========================================================================================================================
18161
18162 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader );
18163
18164 static void SSDPDiscoverReadHandler( void *inContext )
18165 {
18166 OSStatus err;
18167 struct timeval now;
18168 SocketContext * const sockCtx = (SocketContext *) inContext;
18169 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
18170 HTTPHeader * const header = &context->header;
18171 sockaddr_ip fromAddr;
18172 size_t msgLen;
18173
18174 gettimeofday( &now, NULL );
18175
18176 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
18177 NULL, NULL, NULL, NULL );
18178 require_noerr( err, exit );
18179
18180 FPrintF( stdout, "---\n" );
18181 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
18182 FPrintF( stdout, "Source: %##a\n", &fromAddr );
18183 FPrintF( stdout, "Message size: %zu\n", msgLen );
18184 header->len = msgLen;
18185 if( _HTTPHeader_Validate( header ) )
18186 {
18187 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
18188 if( header->extraDataLen > 0 )
18189 {
18190 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
18191 }
18192 }
18193 else
18194 {
18195 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
18196 goto exit;
18197 }
18198
18199 exit:
18200 if( err ) exit( 1 );
18201 }
18202
18203 //===========================================================================================================================
18204 // _HTTPHeader_Validate
18205 //
18206 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
18207 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
18208 //
18209 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
18210 //===========================================================================================================================
18211
18212 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader )
18213 {
18214 const char * src;
18215 const char * end;
18216
18217 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
18218
18219 require( inHeader->len < sizeof( inHeader->buf ), exit );
18220 src = inHeader->buf;
18221 end = src + inHeader->len;
18222 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
18223 {
18224 src += 4;
18225 }
18226 else
18227 {
18228 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
18229 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
18230
18231 for( ;; )
18232 {
18233 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
18234 if( src >= end ) goto exit;
18235 ++src;
18236 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
18237 {
18238 src += 2;
18239 break;
18240 }
18241 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
18242 {
18243 src += 1;
18244 break;
18245 }
18246 }
18247 }
18248 inHeader->extraDataPtr = src;
18249 inHeader->extraDataLen = (size_t)( end - src );
18250 inHeader->len = (size_t)( src - inHeader->buf );
18251 return( true );
18252
18253 exit:
18254 return( false );
18255 }
18256
18257 #if( TARGET_OS_DARWIN )
18258 //===========================================================================================================================
18259 // ResQueryCmd
18260 //===========================================================================================================================
18261
18262 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
18263
18264 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
18265 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
18266 int,
18267 ( const char *dname, int class, int type, u_char *answer, int anslen ),
18268 ( dname, class, type, answer, anslen ) );
18269
18270 // res_query() from libinfo
18271
18272 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
18273 SOFT_LINK_FUNCTION_EX( info, res_query,
18274 int,
18275 ( const char *dname, int class, int type, u_char *answer, int anslen ),
18276 ( dname, class, type, answer, anslen ) );
18277
18278 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
18279
18280 static void ResQueryCmd( void )
18281 {
18282 OSStatus err;
18283 res_query_f res_query_ptr;
18284 int n;
18285 uint16_t type, class;
18286 uint8_t answer[ 1024 ];
18287
18288 // Get pointer to one of the res_query() functions.
18289
18290 if( gResQuery_UseLibInfo )
18291 {
18292 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
18293 {
18294 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
18295 err = kNotFoundErr;
18296 goto exit;
18297 }
18298 res_query_ptr = soft_res_query;
18299 }
18300 else
18301 {
18302 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
18303 {
18304 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
18305 err = kNotFoundErr;
18306 goto exit;
18307 }
18308 res_query_ptr = soft_res_9_query;
18309 }
18310
18311 // Get record type.
18312
18313 err = RecordTypeFromArgString( gResQuery_Type, &type );
18314 require_noerr( err, exit );
18315
18316 // Get record class.
18317
18318 if( gResQuery_Class )
18319 {
18320 err = RecordClassFromArgString( gResQuery_Class, &class );
18321 require_noerr( err, exit );
18322 }
18323 else
18324 {
18325 class = kDNSServiceClass_IN;
18326 }
18327
18328 // Print prologue.
18329
18330 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
18331 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
18332 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
18333 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18334 FPrintF( stdout, "---\n" );
18335
18336 // Call res_query().
18337
18338 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
18339 if( n < 0 )
18340 {
18341 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
18342 err = kUnknownErr;
18343 goto exit;
18344 }
18345
18346 // Print result.
18347
18348 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
18349
18350 exit:
18351 if( err ) exit( 1 );
18352 }
18353
18354 //===========================================================================================================================
18355 // ResolvDNSQueryCmd
18356 //===========================================================================================================================
18357
18358 // 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
18359 // avoid including the header file.
18360
18361 typedef void * dns_handle_t;
18362
18363 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
18364 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
18365 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
18366 int32_t, (
18367 dns_handle_t dns,
18368 const char * name,
18369 uint32_t dnsclass,
18370 uint32_t dnstype,
18371 char * buf,
18372 uint32_t len,
18373 struct sockaddr * from,
18374 uint32_t * fromlen ),
18375 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
18376
18377 static void ResolvDNSQueryCmd( void )
18378 {
18379 OSStatus err;
18380 int n;
18381 dns_handle_t dns = NULL;
18382 uint16_t type, class;
18383 sockaddr_ip from;
18384 uint32_t fromLen;
18385 uint8_t answer[ 1024 ];
18386
18387 // Make sure that the required symbols are available.
18388
18389 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
18390 {
18391 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
18392 err = kNotFoundErr;
18393 goto exit;
18394 }
18395
18396 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
18397 {
18398 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
18399 err = kNotFoundErr;
18400 goto exit;
18401 }
18402
18403 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
18404 {
18405 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
18406 err = kNotFoundErr;
18407 goto exit;
18408 }
18409
18410 // Get record type.
18411
18412 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
18413 require_noerr( err, exit );
18414
18415 // Get record class.
18416
18417 if( gResolvDNSQuery_Class )
18418 {
18419 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
18420 require_noerr( err, exit );
18421 }
18422 else
18423 {
18424 class = kDNSServiceClass_IN;
18425 }
18426
18427 // Get dns handle.
18428
18429 dns = soft_dns_open( gResolvDNSQuery_Path );
18430 if( !dns )
18431 {
18432 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
18433 err = kUnknownErr;
18434 goto exit;
18435 }
18436
18437 // Print prologue.
18438
18439 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
18440 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
18441 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
18442 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
18443 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18444 FPrintF( stdout, "---\n" );
18445
18446 // Call dns_query().
18447
18448 memset( &from, 0, sizeof( from ) );
18449 fromLen = (uint32_t) sizeof( from );
18450 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
18451 &fromLen );
18452 if( n < 0 )
18453 {
18454 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
18455 err = kUnknownErr;
18456 goto exit;
18457 }
18458
18459 // Print result.
18460
18461 FPrintF( stdout, "From: %##a\n", &from );
18462 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
18463
18464 exit:
18465 if( dns ) soft_dns_free( dns );
18466 if( err ) exit( 1 );
18467 }
18468
18469 //===========================================================================================================================
18470 // CFHostCmd
18471 //===========================================================================================================================
18472
18473 static void
18474 _CFHostResolveCallback(
18475 CFHostRef inHost,
18476 CFHostInfoType inInfoType,
18477 const CFStreamError * inError,
18478 void * inInfo );
18479
18480 static void CFHostCmd( void )
18481 {
18482 OSStatus err;
18483 CFStringRef name;
18484 Boolean success;
18485 CFHostRef host = NULL;
18486 CFHostClientContext context;
18487 CFStreamError streamErr;
18488
18489 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
18490 require_action( name, exit, err = kUnknownErr );
18491
18492 host = CFHostCreateWithName( kCFAllocatorDefault, name );
18493 ForgetCF( &name );
18494 require_action( host, exit, err = kUnknownErr );
18495
18496 memset( &context, 0, sizeof( context ) );
18497 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
18498 require_action( success, exit, err = kUnknownErr );
18499
18500 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
18501
18502 // Print prologue.
18503
18504 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
18505 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18506 FPrintF( stdout, "---\n" );
18507
18508 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
18509 require_action( success, exit, err = kUnknownErr );
18510 err = kNoErr;
18511
18512 CFRunLoopRun();
18513
18514 exit:
18515 CFReleaseNullSafe( host );
18516 if( err ) exit( 1 );
18517 }
18518
18519 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
18520 {
18521 OSStatus err;
18522 struct timeval now;
18523
18524 gettimeofday( &now, NULL );
18525
18526 Unused( inInfoType );
18527 Unused( inInfo );
18528
18529 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
18530 {
18531 err = inError->error;
18532 if( inError->domain == kCFStreamErrorDomainNetDB )
18533 {
18534 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
18535 }
18536 else
18537 {
18538 FPrintF( stderr, "Error %#m\n", err );
18539 }
18540 }
18541 else
18542 {
18543 CFArrayRef addresses;
18544 CFIndex count, i;
18545 CFDataRef addrData;
18546 const struct sockaddr * sockAddr;
18547 Boolean wasResolved = false;
18548
18549 addresses = CFHostGetAddressing( inHost, &wasResolved );
18550 check( wasResolved );
18551
18552 if( addresses )
18553 {
18554 count = CFArrayGetCount( addresses );
18555 for( i = 0; i < count; ++i )
18556 {
18557 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
18558 require_noerr( err, exit );
18559
18560 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
18561 FPrintF( stdout, "%##a\n", sockAddr );
18562 }
18563 }
18564 err = kNoErr;
18565 }
18566
18567 FPrintF( stdout, "---\n" );
18568 FPrintF( stdout, "End time: %{du:time}\n", &now );
18569
18570 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
18571
18572 exit:
18573 exit( err ? 1 : 0 );
18574 }
18575
18576 //===========================================================================================================================
18577 // DNSConfigAddCmd
18578 //
18579 // Note: Based on ajn's supplemental test tool.
18580 //===========================================================================================================================
18581
18582 static void DNSConfigAddCmd( void )
18583 {
18584 OSStatus err;
18585 CFMutableDictionaryRef dict = NULL;
18586 CFMutableArrayRef array = NULL;
18587 size_t i;
18588 SCDynamicStoreRef store = NULL;
18589 CFStringRef key = NULL;
18590 Boolean success;
18591
18592 // Create dictionary.
18593
18594 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
18595 require_action( dict, exit, err = kNoMemoryErr );
18596
18597 // Add DNS server IP addresses.
18598
18599 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
18600 require_action( array, exit, err = kNoMemoryErr );
18601
18602 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
18603 {
18604 CFStringRef addrStr;
18605
18606 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
18607 require_action( addrStr, exit, err = kUnknownErr );
18608
18609 CFArrayAppendValue( array, addrStr );
18610 CFRelease( addrStr );
18611 }
18612
18613 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
18614 ForgetCF( &array );
18615
18616 // Add domains, if any.
18617
18618 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
18619 require_action( array, exit, err = kNoMemoryErr );
18620
18621 if( gDNSConfigAdd_DomainCount > 0 )
18622 {
18623 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
18624 {
18625 CFStringRef domainStr;
18626
18627 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
18628 require_action( domainStr, exit, err = kUnknownErr );
18629
18630 CFArrayAppendValue( array, domainStr );
18631 CFRelease( domainStr );
18632 }
18633 }
18634 else
18635 {
18636 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
18637
18638 CFArrayAppendValue( array, CFSTR( "" ) );
18639 }
18640
18641 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
18642 ForgetCF( &array );
18643
18644 // Add interface, if any.
18645
18646 if( gDNSConfigAdd_Interface )
18647 {
18648 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
18649 require_noerr( err, exit );
18650
18651 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
18652 }
18653
18654 // Set dictionary in dynamic store.
18655
18656 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
18657 err = map_scerror( store );
18658 require_noerr( err, exit );
18659
18660 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
18661 require_action( key, exit, err = kUnknownErr );
18662
18663 success = SCDynamicStoreSetValue( store, key, dict );
18664 require_action( success, exit, err = kUnknownErr );
18665
18666 exit:
18667 CFReleaseNullSafe( dict );
18668 CFReleaseNullSafe( array );
18669 CFReleaseNullSafe( store );
18670 CFReleaseNullSafe( key );
18671 gExitCode = err ? 1 : 0;
18672 }
18673
18674 //===========================================================================================================================
18675 // DNSConfigRemoveCmd
18676 //===========================================================================================================================
18677
18678 static void DNSConfigRemoveCmd( void )
18679 {
18680 OSStatus err;
18681 SCDynamicStoreRef store = NULL;
18682 CFStringRef key = NULL;
18683 Boolean success;
18684
18685 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
18686 err = map_scerror( store );
18687 require_noerr( err, exit );
18688
18689 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
18690 require_action( key, exit, err = kUnknownErr );
18691
18692 success = SCDynamicStoreRemoveValue( store, key );
18693 require_action( success, exit, err = kUnknownErr );
18694
18695 exit:
18696 CFReleaseNullSafe( store );
18697 CFReleaseNullSafe( key );
18698 gExitCode = err ? 1 : 0;
18699 }
18700
18701 //===========================================================================================================================
18702 // XPCSendCmd
18703 //===========================================================================================================================
18704
18705 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
18706
18707 static void XPCSendCmd( void )
18708 {
18709 OSStatus err;
18710 xpc_object_t msg, reply;
18711
18712 err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
18713 require_noerr_quiet( err, exit );
18714
18715 FPrintF( stdout, "Service: %s\n", gXPCSend_ServiceName );
18716 FPrintF( stdout, "Message: %s\n", gXPCSend_MessageStr );
18717 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18718 FPrintF( stdout, "---\n" );
18719 FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
18720
18721 err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
18722 xpc_forget( &msg );
18723 require_noerr_quiet( err, exit );
18724
18725 FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
18726 FPrintF( stdout, "---\n" );
18727 FPrintF( stdout, "End time: %{du:time}\n", NULL );
18728 xpc_forget( &reply );
18729
18730 exit:
18731 if( err ) ErrQuit( 1, "error: %#m\n", err );
18732 }
18733
18734 //===========================================================================================================================
18735 // _XPCDictionaryCreateFromString
18736 //===========================================================================================================================
18737
18738 #define kXPCObjectPrefix_Bool "bool:"
18739 #define kXPCObjectPrefix_Data "data:"
18740 #define kXPCObjectPrefix_Int64 "int:"
18741 #define kXPCObjectPrefix_String "string:"
18742 #define kXPCObjectPrefix_UInt64 "uint:"
18743 #define kXPCObjectPrefix_UUID "uuid:"
18744
18745 typedef struct XPCListItem XPCListItem;
18746 struct XPCListItem
18747 {
18748 XPCListItem * next;
18749 xpc_object_t obj;
18750 char * key;
18751 };
18752
18753 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
18754 static void _XPCListItemFree( XPCListItem *inItem );
18755 static void _XPCListFree( XPCListItem *inList );
18756
18757 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject );
18758
18759 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
18760 {
18761 OSStatus err;
18762 xpc_object_t container;
18763 const char * ptr = inString;
18764 const char * const end = inString + strlen( inString );
18765 XPCListItem * list = NULL;
18766
18767 container = xpc_dictionary_create( NULL, NULL, 0 );
18768 require_action( container, exit, err = kNoMemoryErr );
18769
18770 while( *ptr )
18771 {
18772 xpc_type_t containerType;
18773 xpc_object_t value;
18774 int c;
18775 char keyStr[ 256 ];
18776 char valStr[ 256 ];
18777
18778 // At this point, zero or more of the current container's elements have been parsed.
18779 // Skip the white space leading up to the container's next element, if any, or the container's end.
18780
18781 while( isspace_safe( *ptr ) ) ++ptr;
18782
18783 // Check if we're done with the current container.
18784
18785 c = *ptr;
18786 if( c == '\0' ) break;
18787
18788 containerType = xpc_get_type( container );
18789 if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
18790 ( ( containerType == XPC_TYPE_ARRAY ) && ( c == ']' ) ) )
18791 {
18792 XPCListItem * item;
18793
18794 item = list;
18795 require_action_quiet( item, exit, err = kMalformedErr );
18796
18797 ++ptr;
18798
18799 // Add the current container to its parent container.
18800
18801 if( item->key )
18802 {
18803 xpc_dictionary_set_value( item->obj, item->key, container );
18804 }
18805 else
18806 {
18807 xpc_array_append_value( item->obj, container );
18808 }
18809
18810 // Continue with the parent container.
18811
18812 xpc_release( container );
18813 container = xpc_retain( item->obj );
18814 list = item->next;
18815 _XPCListItemFree( item );
18816 continue;
18817 }
18818
18819 // If the current container is a dictionary, parse the key string.
18820
18821 if( containerType == XPC_TYPE_DICTIONARY )
18822 {
18823 err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
18824 require_noerr_quiet( err, exit );
18825
18826 c = *ptr;
18827 require_action_quiet( c == '=', exit, err = kMalformedErr );
18828 ++ptr;
18829 }
18830
18831 // Check if the value is a dictionary ({...}) or an array ([...]).
18832
18833 c = *ptr;
18834 if( ( c == '{' ) || ( c == '[' ) )
18835 {
18836 XPCListItem * item;
18837
18838 ++ptr;
18839
18840 // Save the current container.
18841
18842 err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
18843 require_noerr( err, exit );
18844
18845 item->next = list;
18846 list = item;
18847 item = NULL;
18848
18849 // Create and continue with the child container.
18850
18851 xpc_release( container );
18852 if( c == '{' )
18853 {
18854 container = xpc_dictionary_create( NULL, NULL, 0 );
18855 require_action( container, exit, err = kNoMemoryErr );
18856 }
18857 else
18858 {
18859 container = xpc_array_create( NULL, 0 );
18860 require_action( container, exit, err = kNoMemoryErr );
18861 }
18862 continue;
18863 }
18864
18865 // Parse the value string.
18866
18867 err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
18868 require_noerr_quiet( err, exit );
18869
18870 err = _XPCObjectFromString( valStr, &value );
18871 require_noerr_quiet( err, exit );
18872
18873 if( containerType == XPC_TYPE_DICTIONARY )
18874 {
18875 xpc_dictionary_set_value( container, keyStr, value );
18876 }
18877 else
18878 {
18879 xpc_array_append_value( container, value );
18880 }
18881 xpc_forget( &value );
18882 }
18883 require_action_quiet( !list, exit, err = kMalformedErr );
18884
18885 check( container );
18886 check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
18887
18888 *outDict = container;
18889 container = NULL;
18890 err = kNoErr;
18891
18892 exit:
18893 xpc_release_null_safe( container );
18894 if( list ) _XPCListFree( list );
18895 return( err );
18896 }
18897
18898 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject )
18899 {
18900 OSStatus err;
18901 xpc_object_t object;
18902
18903 if( 0 ) {}
18904
18905 // Bool
18906
18907 else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
18908 {
18909 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Bool );
18910 bool value;
18911
18912 if( IsTrueString( str, kSizeCString ) )
18913 {
18914 value = true;
18915 }
18916 else if( IsFalseString( str, kSizeCString ) )
18917 {
18918 value = false;
18919 }
18920 else
18921 {
18922 err = kValueErr;
18923 goto exit;
18924 }
18925
18926 object = xpc_bool_create( value );
18927 require_action( object, exit, err = kNoMemoryErr );
18928 }
18929
18930 // Data
18931
18932 else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
18933 {
18934 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Data );
18935 uint8_t * dataPtr;
18936 size_t dataLen;
18937
18938 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
18939 require_noerr( err, exit );
18940
18941 object = xpc_data_create( dataPtr, dataLen );
18942 free( dataPtr );
18943 require_action( object, exit, err = kNoMemoryErr );
18944 }
18945
18946 // Int64
18947
18948 else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
18949 {
18950 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
18951 int64_t i64;
18952
18953 i64 = _StringToInt64( str, &err );
18954 require_noerr_quiet( err, exit );
18955
18956 object = xpc_int64_create( i64 );
18957 require_action( object, exit, err = kNoMemoryErr );
18958 }
18959
18960 // String
18961
18962 else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
18963 {
18964 const char * const str = inString + sizeof_string( kXPCObjectPrefix_String );
18965
18966 object = xpc_string_create( str );
18967 require_action( object, exit, err = kNoMemoryErr );
18968 }
18969
18970 // UInt64
18971
18972 else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
18973 {
18974 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
18975 uint64_t u64;
18976
18977 u64 = _StringToUInt64( str, &err );
18978 require_noerr_quiet( err, exit );
18979
18980 object = xpc_uint64_create( u64 );
18981 require_action( object, exit, err = kNoMemoryErr );
18982 }
18983
18984 // UUID
18985
18986 else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
18987 {
18988 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UUID );
18989 uuid_t uuid;
18990
18991 err = uuid_parse( str, uuid );
18992 require_noerr_action_quiet( err, exit, err = kValueErr );
18993
18994 object = xpc_uuid_create( uuid );
18995 require_action( object, exit, err = kNoMemoryErr );
18996 }
18997
18998 // Unsupported prefix
18999
19000 else
19001 {
19002 err = kValueErr;
19003 goto exit;
19004 }
19005
19006 *outObject = object;
19007 err = kNoErr;
19008
19009 exit:
19010 return( err );
19011 }
19012
19013 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
19014 {
19015 OSStatus err;
19016 XPCListItem * item;
19017
19018 item = (XPCListItem *) calloc( 1, sizeof( *item ) );
19019 require_action( item, exit, err = kNoMemoryErr );
19020
19021 item->obj = xpc_retain( inObject );
19022 if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
19023 {
19024 item->key = strdup( inKey );
19025 require_action( item->key, exit, err = kNoMemoryErr );
19026 }
19027
19028 *outItem = item;
19029 item = NULL;
19030 err = kNoErr;
19031
19032 exit:
19033 if( item ) _XPCListItemFree( item );
19034 return( err );
19035 }
19036
19037 static void _XPCListItemFree( XPCListItem *inItem )
19038 {
19039 xpc_forget( &inItem->obj );
19040 ForgetMem( &inItem->key );
19041 free( inItem );
19042 }
19043
19044 static void _XPCListFree( XPCListItem *inList )
19045 {
19046 XPCListItem * item;
19047
19048 while( ( item = inList ) != NULL )
19049 {
19050 inList = item->next;
19051 _XPCListItemFree( item );
19052 }
19053 }
19054 #endif // TARGET_OS_DARWIN
19055
19056 #if( MDNSRESPONDER_PROJECT )
19057 //===========================================================================================================================
19058 // InterfaceMonitorCmd
19059 //===========================================================================================================================
19060
19061 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
19062 static void _InterfaceMonitorSignalHandler( void *inContext );
19063
19064 static void InterfaceMonitorCmd( void )
19065 {
19066 OSStatus err;
19067 mdns_interface_monitor_t monitor;
19068 dispatch_source_t signalSource = NULL;
19069 uint32_t ifIndex;
19070 __block int exitCode;
19071
19072 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
19073 require_noerr_quiet( err, exit );
19074
19075 monitor = mdns_interface_monitor_create( ifIndex );
19076 require_action( monitor, exit, err = kNoResourcesErr );
19077
19078 exitCode = 0;
19079 mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
19080 mdns_interface_monitor_set_event_handler( monitor,
19081 ^( mdns_event_t inEvent, OSStatus inError )
19082 {
19083 switch( inEvent )
19084 {
19085 case mdns_event_error:
19086 FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
19087 mdns_interface_monitor_invalidate( monitor );
19088 exitCode = 1;
19089 break;
19090
19091 case mdns_event_invalidated:
19092 FPrintF( stdout, "Interface monitor invalidated.\n" );
19093 mdns_release( monitor );
19094 exit( exitCode );
19095
19096 default:
19097 FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
19098 break;
19099 }
19100 } );
19101 mdns_interface_monitor_set_update_handler( monitor,
19102 ^( __unused mdns_interface_flags_t inChangeFlags )
19103 {
19104 _InterfaceMonitorPrint( monitor );
19105 } );
19106
19107 _InterfaceMonitorPrint( monitor );
19108 mdns_interface_monitor_activate( monitor );
19109
19110 signal( SIGINT, SIG_IGN );
19111 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _InterfaceMonitorSignalHandler, monitor,
19112 &signalSource );
19113 require_noerr( err, exit );
19114 dispatch_resume( signalSource );
19115
19116 dispatch_main();
19117
19118 exit:
19119 if( err ) ErrQuit( 1, "error: %#m\n", err );
19120 }
19121
19122 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
19123 {
19124 FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
19125 }
19126
19127 static void _InterfaceMonitorSignalHandler( void *inContext )
19128 {
19129 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
19130 }
19131
19132 //===========================================================================================================================
19133 // DNSProxyCmd
19134 //===========================================================================================================================
19135
19136 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
19137 static void _DNSProxyCmdSignalHandler( void *inContext );
19138
19139 static void DNSProxyCmd( void )
19140 {
19141 OSStatus err;
19142 size_t i;
19143 DNSXConnRef connection;
19144 IfIndex inputIfIndexes[ MaxInputIf ];
19145 dispatch_source_t sigIntSource = NULL;
19146 dispatch_source_t sigTermSource = NULL;
19147 uint32_t outputIfIndex;
19148 char ifName[ kInterfaceNameBufLen ];
19149
19150 if( gDNSProxy_InputInterfaceCount > MaxInputIf )
19151 {
19152 FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
19153 gDNSProxy_InputInterfaceCount, MaxInputIf );
19154 err = kRangeErr;
19155 goto exit;
19156 }
19157
19158 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
19159 {
19160 uint32_t ifIndex;
19161
19162 err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
19163 require_noerr_quiet( err, exit );
19164
19165 inputIfIndexes[ i ] = ifIndex;
19166 }
19167 while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0; // Remaining interface indexes are required to be 0.
19168
19169 if( gDNSProxy_OutputInterface )
19170 {
19171 err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
19172 require_noerr_quiet( err, exit );
19173 }
19174 else
19175 {
19176 outputIfIndex = kDNSIfindexAny;
19177 }
19178
19179 FPrintF( stdout, "Input Interfaces:" );
19180 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
19181 {
19182 const uint32_t ifIndex = (uint32_t) inputIfIndexes[ i ];
19183
19184 FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
19185 }
19186 FPrintF( stdout, "\n" );
19187 FPrintF( stdout, "Output Interface: %u (%s)\n", outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
19188 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
19189 FPrintF( stdout, "---\n" );
19190
19191 connection = NULL;
19192 err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex, dispatch_get_main_queue(),
19193 _DNSProxyCallback );
19194 require_noerr_quiet( err, exit );
19195
19196 signal( SIGINT, SIG_IGN );
19197 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
19198 &sigIntSource );
19199 require_noerr( err, exit );
19200 dispatch_activate( sigIntSource );
19201
19202 signal( SIGTERM, SIG_IGN );
19203 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
19204 &sigTermSource );
19205 require_noerr( err, exit );
19206 dispatch_activate( sigTermSource );
19207
19208 dispatch_main();
19209
19210 exit:
19211 if( err ) ErrQuit( 1, "error: %#m\n", err );
19212 }
19213
19214 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
19215 {
19216 Unused( inConnection );
19217
19218 if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
19219 }
19220
19221 static void _DNSProxyCmdSignalHandler( void *inContext )
19222 {
19223 DNSXConnRef const connection = (DNSXConnRef) inContext;
19224 struct timeval now;
19225
19226 gettimeofday( &now, NULL );
19227
19228 DNSXRefDeAlloc( connection );
19229
19230 FPrintF( stdout, "---\n" );
19231 FPrintF( stdout, "End time: %{du:time}\n", &now );
19232 exit( 0 );
19233 }
19234
19235 //===========================================================================================================================
19236 // XCTestCmd
19237 //===========================================================================================================================
19238
19239 static void XCTestCmd( void )
19240 {
19241 int result = 0;
19242 setenv(DNSSDUTIL_XCTEST, DNSSDUTIL_XCTEST, 0);
19243 if(!TestUtilsRunXCTestNamed(gXCTest_Classname)) {
19244 result = 1;
19245 }
19246 unsetenv(DNSSDUTIL_XCTEST);
19247 exit( result );
19248 }
19249
19250 #endif // MDNSRESPONDER_PROJECT
19251
19252 //===========================================================================================================================
19253 // DaemonVersionCmd
19254 //===========================================================================================================================
19255
19256 static void DaemonVersionCmd( void )
19257 {
19258 OSStatus err;
19259 uint32_t size, version;
19260 char strBuf[ 16 ];
19261
19262 size = (uint32_t) sizeof( version );
19263 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
19264 require_noerr( err, exit );
19265
19266 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
19267
19268 exit:
19269 if( err ) exit( 1 );
19270 }
19271
19272 //===========================================================================================================================
19273 // Exit
19274 //===========================================================================================================================
19275
19276 static void Exit( void *inContext )
19277 {
19278 const char * const reason = (const char *) inContext;
19279
19280 FPrintF( stdout, "---\n" );
19281 FPrintF( stdout, "End time: %{du:time}\n", NULL );
19282 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
19283 exit( gExitCode );
19284 }
19285
19286 //===========================================================================================================================
19287 // _PrintFExtensionTimestampHandler
19288 //===========================================================================================================================
19289
19290 static int
19291 _PrintFExtensionTimestampHandler(
19292 PrintFContext * inContext,
19293 PrintFFormat * inFormat,
19294 PrintFVAList * inArgs,
19295 void * inUserContext )
19296 {
19297 struct timeval now;
19298 const struct timeval * tv;
19299 struct tm * localTime;
19300 size_t len;
19301 int n;
19302 char dateTimeStr[ 32 ];
19303
19304 Unused( inUserContext );
19305
19306 tv = va_arg( inArgs->args, const struct timeval * );
19307 require_action_quiet( !inFormat->suppress, exit, n = 0 );
19308
19309 if( !tv )
19310 {
19311 gettimeofday( &now, NULL );
19312 tv = &now;
19313 }
19314 localTime = localtime( &tv->tv_sec );
19315 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
19316 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
19317
19318 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
19319
19320 exit:
19321 return( n );
19322 }
19323
19324 //===========================================================================================================================
19325 // _PrintFExtensionDNSMessageHandler
19326 //===========================================================================================================================
19327
19328 static int
19329 _PrintFExtensionDNSMessageHandler(
19330 PrintFContext * inContext,
19331 PrintFFormat * inFormat,
19332 PrintFVAList * inArgs,
19333 void * inUserContext )
19334 {
19335 OSStatus err;
19336 const void * msgPtr;
19337 size_t msgLen;
19338 char * text;
19339 int n;
19340 Boolean isMDNS;
19341 Boolean printRawRData;
19342
19343 Unused( inUserContext );
19344
19345 msgPtr = va_arg( inArgs->args, const void * );
19346 msgLen = va_arg( inArgs->args, size_t );
19347 require_action_quiet( !inFormat->suppress, exit, n = 0 );
19348
19349 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
19350 printRawRData = ( inFormat->precision > 0 ) ? true : false;
19351 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
19352 if( !err )
19353 {
19354 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
19355 free( text );
19356 }
19357 else
19358 {
19359 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
19360 }
19361
19362 exit:
19363 return( n );
19364 }
19365
19366 //===========================================================================================================================
19367 // _PrintFExtensionCallbackFlagsHandler
19368 //===========================================================================================================================
19369
19370 static int
19371 _PrintFExtensionCallbackFlagsHandler(
19372 PrintFContext * inContext,
19373 PrintFFormat * inFormat,
19374 PrintFVAList * inArgs,
19375 void * inUserContext )
19376 {
19377 DNSServiceFlags flags;
19378 int n;
19379
19380 Unused( inUserContext );
19381
19382 flags = va_arg( inArgs->args, DNSServiceFlags );
19383 require_action_quiet( !inFormat->suppress, exit, n = 0 );
19384
19385 n = PrintFCore( inContext, "%08X %s%c %c%c",
19386 flags, DNSServiceFlagsToAddRmvStr( flags ),
19387 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
19388 ( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
19389 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '*' : ' ' );
19390
19391 exit:
19392 return( n );
19393 }
19394
19395 //===========================================================================================================================
19396 // _PrintFExtensionDNSRecordDataHandler
19397 //===========================================================================================================================
19398
19399 static int
19400 _PrintFExtensionDNSRecordDataHandler(
19401 PrintFContext * inContext,
19402 PrintFFormat * inFormat,
19403 PrintFVAList * inArgs,
19404 void * inUserContext )
19405 {
19406 const void * rdataPtr;
19407 unsigned int rdataLen, rdataType;
19408 int n, fieldWidth;
19409
19410 Unused( inUserContext );
19411
19412 rdataType = va_arg( inArgs->args, unsigned int );
19413 rdataPtr = va_arg( inArgs->args, const void * );
19414 rdataLen = va_arg( inArgs->args, unsigned int );
19415 require_action_quiet( !inFormat->suppress, exit, n = 0 );
19416
19417 check( inFormat->fieldWidth < INT_MAX );
19418 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
19419
19420 if( rdataLen > 0 )
19421 {
19422 char * rdataStr = NULL;
19423
19424 DNSRecordDataToString( rdataPtr, rdataLen, rdataType, NULL, 0, &rdataStr );
19425 if( rdataStr )
19426 {
19427 n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
19428 free( rdataStr );
19429 }
19430 else
19431 {
19432 n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
19433 }
19434 }
19435 else
19436 {
19437 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
19438 }
19439
19440 exit:
19441 return( n );
19442 }
19443
19444 //===========================================================================================================================
19445 // GetDNSSDFlagsFromOpts
19446 //===========================================================================================================================
19447
19448 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
19449 {
19450 DNSServiceFlags flags;
19451
19452 flags = (DNSServiceFlags) gDNSSDFlags;
19453 if( flags & kDNSServiceFlagsShareConnection )
19454 {
19455 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
19456 kDNSServiceFlagsShareConnection );
19457 }
19458
19459 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
19460 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
19461 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
19462 if( gDNSSDFlag_DenyConstrained ) flags |= kDNSServiceFlagsDenyConstrained;
19463 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
19464 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
19465 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
19466 if( gDNSSDFlag_KnownUnique ) flags |= kDNSServiceFlagsKnownUnique;
19467 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
19468 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
19469 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
19470 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
19471 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
19472 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
19473 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
19474 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
19475 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
19476 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
19477
19478 return( flags );
19479 }
19480
19481 //===========================================================================================================================
19482 // CreateConnectionFromArgString
19483 //===========================================================================================================================
19484
19485 static OSStatus
19486 CreateConnectionFromArgString(
19487 const char * inString,
19488 dispatch_queue_t inQueue,
19489 DNSServiceRef * outSDRef,
19490 ConnectionDesc * outDesc )
19491 {
19492 OSStatus err;
19493 DNSServiceRef sdRef = NULL;
19494 ConnectionType type;
19495 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
19496 uint8_t uuid[ 16 ];
19497
19498 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
19499 {
19500 err = DNSServiceCreateConnection( &sdRef );
19501 require_noerr( err, exit );
19502 type = kConnectionType_Normal;
19503 }
19504 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
19505 {
19506 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
19507
19508 err = StringToInt32( pidStr, &pid );
19509 if( err )
19510 {
19511 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
19512 err = kParamErr;
19513 goto exit;
19514 }
19515
19516 memset( uuid, 0, sizeof( uuid ) );
19517 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
19518 if( err )
19519 {
19520 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
19521 goto exit;
19522 }
19523 type = kConnectionType_DelegatePID;
19524 }
19525 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
19526 {
19527 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
19528
19529 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
19530
19531 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
19532 if( err )
19533 {
19534 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
19535 err = kParamErr;
19536 goto exit;
19537 }
19538
19539 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
19540 if( err )
19541 {
19542 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
19543 goto exit;
19544 }
19545 type = kConnectionType_DelegateUUID;
19546 }
19547 else
19548 {
19549 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
19550 err = kParamErr;
19551 goto exit;
19552 }
19553
19554 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
19555 require_noerr( err, exit );
19556
19557 *outSDRef = sdRef;
19558 if( outDesc )
19559 {
19560 outDesc->type = type;
19561 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
19562 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
19563 }
19564 sdRef = NULL;
19565
19566 exit:
19567 if( sdRef ) DNSServiceRefDeallocate( sdRef );
19568 return( err );
19569 }
19570
19571 //===========================================================================================================================
19572 // InterfaceIndexFromArgString
19573 //===========================================================================================================================
19574
19575 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
19576 {
19577 OSStatus err;
19578 uint32_t ifIndex;
19579
19580 if( inString )
19581 {
19582 ifIndex = if_nametoindex( inString );
19583 if( ifIndex == 0 )
19584 {
19585 err = StringToUInt32( inString, &ifIndex );
19586 if( err )
19587 {
19588 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
19589 err = kParamErr;
19590 goto exit;
19591 }
19592 }
19593 }
19594 else
19595 {
19596 ifIndex = 0;
19597 }
19598
19599 *outIndex = ifIndex;
19600 err = kNoErr;
19601
19602 exit:
19603 return( err );
19604 }
19605
19606 //===========================================================================================================================
19607 // RecordDataFromArgString
19608 //===========================================================================================================================
19609
19610 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
19611 {
19612 OSStatus err;
19613 uint8_t * dataPtr = NULL;
19614 size_t dataLen;
19615
19616 if( 0 ) {}
19617
19618 // Domain name
19619
19620 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
19621 {
19622 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
19623
19624 err = StringToDomainName( str, &dataPtr, &dataLen );
19625 require_noerr_quiet( err, exit );
19626 }
19627
19628 // File path
19629
19630 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
19631 {
19632 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
19633
19634 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
19635 require_noerr( err, exit );
19636 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
19637 }
19638
19639 // Hexadecimal string
19640
19641 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
19642 {
19643 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
19644
19645 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
19646 require_noerr( err, exit );
19647 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
19648 }
19649
19650 // IPv4 address string
19651
19652 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
19653 {
19654 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
19655
19656 err = StringToARecordData( str, &dataPtr, &dataLen );
19657 require_noerr_quiet( err, exit );
19658 }
19659
19660 // IPv6 address string
19661
19662 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
19663 {
19664 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
19665
19666 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
19667 require_noerr_quiet( err, exit );
19668 }
19669
19670 // SRV record
19671
19672 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
19673 {
19674 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
19675
19676 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
19677 require_noerr( err, exit );
19678 }
19679
19680 // String with escaped hex and octal bytes
19681
19682 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
19683 {
19684 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
19685 const char * const end = str + strlen( str );
19686 size_t copiedLen;
19687 size_t totalLen;
19688 Boolean success;
19689
19690 if( str < end )
19691 {
19692 success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
19693 require_action( success, exit, err = kParamErr );
19694 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
19695
19696 dataLen = totalLen;
19697 dataPtr = (uint8_t *) malloc( dataLen );
19698 require_action( dataPtr, exit, err = kNoMemoryErr );
19699
19700 success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
19701 require_action( success, exit, err = kParamErr );
19702 check( copiedLen == dataLen );
19703 }
19704 else
19705 {
19706 dataPtr = NULL;
19707 dataLen = 0;
19708 }
19709 }
19710
19711 // TXT record
19712
19713 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
19714 {
19715 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
19716
19717 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
19718 require_noerr( err, exit );
19719 }
19720
19721 // Unrecognized format
19722
19723 else
19724 {
19725 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
19726 err = kParamErr;
19727 goto exit;
19728 }
19729
19730 err = kNoErr;
19731 *outDataLen = dataLen;
19732 *outDataPtr = dataPtr;
19733 dataPtr = NULL;
19734
19735 exit:
19736 FreeNullSafe( dataPtr );
19737 return( err );
19738 }
19739
19740 //===========================================================================================================================
19741 // RecordTypeFromArgString
19742 //===========================================================================================================================
19743
19744 typedef struct
19745 {
19746 uint16_t value; // Record type's numeric value.
19747 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
19748
19749 } RecordType;
19750
19751 static const RecordType kRecordTypes[] =
19752 {
19753 // Common types.
19754
19755 { kDNSServiceType_A, "A" },
19756 { kDNSServiceType_AAAA, "AAAA" },
19757 { kDNSServiceType_PTR, "PTR" },
19758 { kDNSServiceType_SRV, "SRV" },
19759 { kDNSServiceType_TXT, "TXT" },
19760 { kDNSServiceType_CNAME, "CNAME" },
19761 { kDNSServiceType_SOA, "SOA" },
19762 { kDNSServiceType_NSEC, "NSEC" },
19763 { kDNSServiceType_NS, "NS" },
19764 { kDNSServiceType_MX, "MX" },
19765 { kDNSServiceType_ANY, "ANY" },
19766 { kDNSServiceType_OPT, "OPT" },
19767
19768 // Less common types.
19769
19770 { kDNSServiceType_MD, "MD" },
19771 { kDNSServiceType_NS, "NS" },
19772 { kDNSServiceType_MD, "MD" },
19773 { kDNSServiceType_MF, "MF" },
19774 { kDNSServiceType_MB, "MB" },
19775 { kDNSServiceType_MG, "MG" },
19776 { kDNSServiceType_MR, "MR" },
19777 { kDNSServiceType_NULL, "NULL" },
19778 { kDNSServiceType_WKS, "WKS" },
19779 { kDNSServiceType_HINFO, "HINFO" },
19780 { kDNSServiceType_MINFO, "MINFO" },
19781 { kDNSServiceType_RP, "RP" },
19782 { kDNSServiceType_AFSDB, "AFSDB" },
19783 { kDNSServiceType_X25, "X25" },
19784 { kDNSServiceType_ISDN, "ISDN" },
19785 { kDNSServiceType_RT, "RT" },
19786 { kDNSServiceType_NSAP, "NSAP" },
19787 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
19788 { kDNSServiceType_SIG, "SIG" },
19789 { kDNSServiceType_KEY, "KEY" },
19790 { kDNSServiceType_PX, "PX" },
19791 { kDNSServiceType_GPOS, "GPOS" },
19792 { kDNSServiceType_LOC, "LOC" },
19793 { kDNSServiceType_NXT, "NXT" },
19794 { kDNSServiceType_EID, "EID" },
19795 { kDNSServiceType_NIMLOC, "NIMLOC" },
19796 { kDNSServiceType_ATMA, "ATMA" },
19797 { kDNSServiceType_NAPTR, "NAPTR" },
19798 { kDNSServiceType_KX, "KX" },
19799 { kDNSServiceType_CERT, "CERT" },
19800 { kDNSServiceType_A6, "A6" },
19801 { kDNSServiceType_DNAME, "DNAME" },
19802 { kDNSServiceType_SINK, "SINK" },
19803 { kDNSServiceType_APL, "APL" },
19804 { kDNSServiceType_DS, "DS" },
19805 { kDNSServiceType_SSHFP, "SSHFP" },
19806 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
19807 { kDNSServiceType_RRSIG, "RRSIG" },
19808 { kDNSServiceType_DNSKEY, "DNSKEY" },
19809 { kDNSServiceType_DHCID, "DHCID" },
19810 { kDNSServiceType_NSEC3, "NSEC3" },
19811 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
19812 { kDNSServiceType_HIP, "HIP" },
19813 { kDNSServiceType_SPF, "SPF" },
19814 { kDNSServiceType_UINFO, "UINFO" },
19815 { kDNSServiceType_UID, "UID" },
19816 { kDNSServiceType_GID, "GID" },
19817 { kDNSServiceType_UNSPEC, "UNSPEC" },
19818 { kDNSServiceType_TKEY, "TKEY" },
19819 { kDNSServiceType_TSIG, "TSIG" },
19820 { kDNSServiceType_IXFR, "IXFR" },
19821 { kDNSServiceType_AXFR, "AXFR" },
19822 { kDNSServiceType_MAILB, "MAILB" },
19823 { kDNSServiceType_MAILA, "MAILA" }
19824 };
19825
19826 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
19827 {
19828 OSStatus err;
19829 int32_t i32;
19830 const RecordType * type;
19831 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
19832
19833 for( type = kRecordTypes; type < end; ++type )
19834 {
19835 if( strcasecmp( type->name, inString ) == 0 )
19836 {
19837 *outValue = type->value;
19838 return( kNoErr );
19839 }
19840 }
19841
19842 err = StringToInt32( inString, &i32 );
19843 require_noerr_quiet( err, exit );
19844 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
19845
19846 *outValue = (uint16_t) i32;
19847
19848 exit:
19849 return( err );
19850 }
19851
19852 //===========================================================================================================================
19853 // RecordClassFromArgString
19854 //===========================================================================================================================
19855
19856 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
19857 {
19858 OSStatus err;
19859 int32_t i32;
19860
19861 if( strcasecmp( inString, "IN" ) == 0 )
19862 {
19863 *outValue = kDNSServiceClass_IN;
19864 err = kNoErr;
19865 goto exit;
19866 }
19867
19868 err = StringToInt32( inString, &i32 );
19869 require_noerr_quiet( err, exit );
19870 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
19871
19872 *outValue = (uint16_t) i32;
19873
19874 exit:
19875 return( err );
19876 }
19877
19878 //===========================================================================================================================
19879 // InterfaceIndexToName
19880 //===========================================================================================================================
19881
19882 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
19883 {
19884 switch( inIfIndex )
19885 {
19886 case kDNSServiceInterfaceIndexAny:
19887 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
19888 break;
19889
19890 case kDNSServiceInterfaceIndexLocalOnly:
19891 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
19892 break;
19893
19894 case kDNSServiceInterfaceIndexUnicast:
19895 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
19896 break;
19897
19898 case kDNSServiceInterfaceIndexP2P:
19899 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
19900 break;
19901
19902 #if( defined( kDNSServiceInterfaceIndexBLE ) )
19903 case kDNSServiceInterfaceIndexBLE:
19904 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
19905 break;
19906 #endif
19907
19908 default:
19909 {
19910 const char * name;
19911
19912 name = if_indextoname( inIfIndex, inNameBuf );
19913 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
19914 break;
19915 }
19916 }
19917
19918 return( inNameBuf );
19919 }
19920
19921 //===========================================================================================================================
19922 // RecordTypeToString
19923 //===========================================================================================================================
19924
19925 static const char * RecordTypeToString( unsigned int inValue )
19926 {
19927 const RecordType * type;
19928 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
19929
19930 for( type = kRecordTypes; type < end; ++type )
19931 {
19932 if( type->value == inValue ) return( type->name );
19933 }
19934 return( "???" );
19935 }
19936
19937 //===========================================================================================================================
19938 // DNSRecordDataToString
19939 //===========================================================================================================================
19940
19941 static OSStatus
19942 DNSRecordDataToString(
19943 const void * inRDataPtr,
19944 size_t inRDataLen,
19945 unsigned int inRDataType,
19946 const void * inMsgPtr,
19947 size_t inMsgLen,
19948 char ** outString )
19949 {
19950 OSStatus err;
19951 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
19952 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
19953 char * rdataStr;
19954 const uint8_t * ptr;
19955 int n;
19956 char domainNameStr[ kDNSServiceMaxDomainName ];
19957
19958 rdataStr = NULL;
19959
19960 // A Record
19961
19962 if( inRDataType == kDNSServiceType_A )
19963 {
19964 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
19965
19966 ASPrintF( &rdataStr, "%.4a", rdataPtr );
19967 require_action( rdataStr, exit, err = kNoMemoryErr );
19968 }
19969
19970 // AAAA Record
19971
19972 else if( inRDataType == kDNSServiceType_AAAA )
19973 {
19974 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
19975
19976 ASPrintF( &rdataStr, "%.16a", rdataPtr );
19977 require_action( rdataStr, exit, err = kNoMemoryErr );
19978 }
19979
19980 // PTR, CNAME, or NS Record
19981
19982 else if( ( inRDataType == kDNSServiceType_PTR ) ||
19983 ( inRDataType == kDNSServiceType_CNAME ) ||
19984 ( inRDataType == kDNSServiceType_NS ) )
19985 {
19986 if( inMsgPtr )
19987 {
19988 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
19989 require_noerr( err, exit );
19990 }
19991 else
19992 {
19993 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
19994 require_noerr( err, exit );
19995 }
19996
19997 rdataStr = strdup( domainNameStr );
19998 require_action( rdataStr, exit, err = kNoMemoryErr );
19999 }
20000
20001 // SRV Record
20002
20003 else if( inRDataType == kDNSServiceType_SRV )
20004 {
20005 const dns_fixed_fields_srv * fields;
20006 const uint8_t * target;
20007 unsigned int priority, weight, port;
20008
20009 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
20010
20011 fields = (const dns_fixed_fields_srv *) rdataPtr;
20012 priority = dns_fixed_fields_srv_get_priority( fields );
20013 weight = dns_fixed_fields_srv_get_weight( fields );
20014 port = dns_fixed_fields_srv_get_port( fields );
20015 target = (const uint8_t *) &fields[ 1 ];
20016
20017 if( inMsgPtr )
20018 {
20019 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
20020 require_noerr( err, exit );
20021 }
20022 else
20023 {
20024 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
20025 require_noerr( err, exit );
20026 }
20027
20028 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
20029 require_action( rdataStr, exit, err = kNoMemoryErr );
20030 }
20031
20032 // TXT Record
20033
20034 else if( inRDataType == kDNSServiceType_TXT )
20035 {
20036 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
20037
20038 if( inRDataLen == 1 )
20039 {
20040 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
20041 require_action( rdataStr, exit, err = kNoMemoryErr );
20042 }
20043 else
20044 {
20045 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
20046 require_action( rdataStr, exit, err = kNoMemoryErr );
20047 }
20048 }
20049
20050 // SOA Record
20051
20052 else if( inRDataType == kDNSServiceType_SOA )
20053 {
20054 const dns_fixed_fields_soa * fields;
20055 uint32_t serial, refresh, retry, expire, minimum;
20056
20057 if( inMsgPtr )
20058 {
20059 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
20060 require_noerr( err, exit );
20061
20062 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
20063
20064 rdataStr = strdup( domainNameStr );
20065 require_action( rdataStr, exit, err = kNoMemoryErr );
20066
20067 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
20068 require_noerr( err, exit );
20069 }
20070 else
20071 {
20072 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
20073 require_noerr( err, exit );
20074
20075 rdataStr = strdup( domainNameStr );
20076 require_action( rdataStr, exit, err = kNoMemoryErr );
20077
20078 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
20079 require_noerr( err, exit );
20080 }
20081
20082 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
20083
20084 fields = (const dns_fixed_fields_soa *) ptr;
20085 serial = dns_fixed_fields_soa_get_serial( fields );
20086 refresh = dns_fixed_fields_soa_get_refresh( fields );
20087 retry = dns_fixed_fields_soa_get_retry( fields );
20088 expire = dns_fixed_fields_soa_get_expire( fields );
20089 minimum = dns_fixed_fields_soa_get_minimum( fields );
20090
20091 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
20092 require_action( n > 0, exit, err = kUnknownErr );
20093 }
20094
20095 // NSEC Record
20096
20097 else if( inRDataType == kDNSServiceType_NSEC )
20098 {
20099 unsigned int windowBlock, bitmapLen, i, recordType;
20100 const uint8_t * bitmapPtr;
20101
20102 if( inMsgPtr )
20103 {
20104 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
20105 require_noerr( err, exit );
20106 }
20107 else
20108 {
20109 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
20110 require_noerr( err, exit );
20111 }
20112
20113 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
20114
20115 rdataStr = strdup( domainNameStr );
20116 require_action( rdataStr, exit, err = kNoMemoryErr );
20117
20118 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
20119 {
20120 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
20121
20122 windowBlock = ptr[ 0 ];
20123 bitmapLen = ptr[ 1 ];
20124 bitmapPtr = &ptr[ 2 ];
20125
20126 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
20127 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
20128
20129 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
20130 {
20131 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
20132 {
20133 recordType = ( windowBlock * 256 ) + i;
20134 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
20135 require_action( n > 0, exit, err = kUnknownErr );
20136 }
20137 }
20138 }
20139 }
20140
20141 // MX Record
20142
20143 else if( inRDataType == kDNSServiceType_MX )
20144 {
20145 uint16_t preference;
20146 const uint8_t * exchange;
20147
20148 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
20149
20150 preference = ReadBig16( rdataPtr );
20151 exchange = &rdataPtr[ 2 ];
20152
20153 if( inMsgPtr )
20154 {
20155 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
20156 require_noerr( err, exit );
20157 }
20158 else
20159 {
20160 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
20161 require_noerr( err, exit );
20162 }
20163
20164 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
20165 require_action( n > 0, exit, err = kUnknownErr );
20166 }
20167
20168 // Unhandled record type
20169
20170 else
20171 {
20172 err = kNotHandledErr;
20173 goto exit;
20174 }
20175
20176 check( rdataStr );
20177 *outString = rdataStr;
20178 rdataStr = NULL;
20179 err = kNoErr;
20180
20181 exit:
20182 FreeNullSafe( rdataStr );
20183 return( err );
20184 }
20185
20186 //===========================================================================================================================
20187 // DNSMessageToText
20188 //===========================================================================================================================
20189
20190 #define DNSFlagsOpCodeToString( X ) ( \
20191 ( (X) == kDNSOpCode_Query ) ? "Query" : \
20192 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
20193 ( (X) == kDNSOpCode_Status ) ? "Status" : \
20194 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
20195 ( (X) == kDNSOpCode_Update ) ? "Update" : \
20196 "Unassigned" )
20197
20198 #define DNSFlagsRCodeToString( X ) ( \
20199 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
20200 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
20201 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
20202 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
20203 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
20204 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
20205 "???" )
20206
20207 static OSStatus
20208 DNSMessageToText(
20209 const uint8_t * inMsgPtr,
20210 size_t inMsgLen,
20211 const Boolean inMDNS,
20212 const Boolean inPrintRaw,
20213 char ** outText )
20214 {
20215 OSStatus err;
20216 DataBuffer dataBuf;
20217 size_t len;
20218 const DNSHeader * hdr;
20219 const uint8_t * ptr;
20220 unsigned int id, flags, opcode, rcode;
20221 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
20222 uint8_t name[ kDomainNameLengthMax ];
20223 char nameStr[ kDNSServiceMaxDomainName ];
20224
20225 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
20226 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
20227
20228 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
20229
20230 hdr = (DNSHeader *) inMsgPtr;
20231 id = DNSHeaderGetID( hdr );
20232 flags = DNSHeaderGetFlags( hdr );
20233 questionCount = DNSHeaderGetQuestionCount( hdr );
20234 answerCount = DNSHeaderGetAnswerCount( hdr );
20235 authorityCount = DNSHeaderGetAuthorityCount( hdr );
20236 additionalCount = DNSHeaderGetAdditionalCount( hdr );
20237 opcode = DNSFlagsGetOpCode( flags );
20238 rcode = DNSFlagsGetRCode( flags );
20239
20240 _Append( "ID: 0x%04X (%u)\n", id, id );
20241 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
20242 flags,
20243 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
20244 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
20245 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
20246 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
20247 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
20248 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
20249 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
20250 DNSFlagsRCodeToString( rcode ) );
20251 _Append( "Question count: %u\n", questionCount );
20252 _Append( "Answer count: %u\n", answerCount );
20253 _Append( "Authority count: %u\n", authorityCount );
20254 _Append( "Additional count: %u\n", additionalCount );
20255
20256 ptr = (const uint8_t *) &hdr[ 1 ];
20257 for( i = 0; i < questionCount; ++i )
20258 {
20259 uint16_t qtype, qclass;
20260 Boolean isQU;
20261
20262 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
20263 require_noerr( err, exit );
20264
20265 err = DomainNameToString( name, NULL, nameStr, NULL );
20266 require_noerr( err, exit );
20267
20268 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
20269 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
20270
20271 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
20272
20273 _Append( "%-30s %2s %?2s%?2u %-5s\n",
20274 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
20275 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
20276 }
20277
20278 totalRRCount = answerCount + authorityCount + additionalCount;
20279 for( i = 0; i < totalRRCount; ++i )
20280 {
20281 uint16_t type;
20282 uint16_t class;
20283 uint32_t ttl;
20284 const uint8_t * rdataPtr;
20285 size_t rdataLen;
20286 char * rdataStr;
20287 Boolean cacheFlush;
20288
20289 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
20290 require_noerr( err, exit );
20291
20292 err = DomainNameToString( name, NULL, nameStr, NULL );
20293 require_noerr( err, exit );
20294
20295 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
20296 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
20297
20298 rdataStr = NULL;
20299 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
20300 if( !rdataStr )
20301 {
20302 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, (int) rdataLen );
20303 require_action( rdataStr, exit, err = kNoMemoryErr );
20304 }
20305
20306 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
20307 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
20308 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
20309
20310 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
20311 nameStr, ttl, cacheFlush ? "CF" : "",
20312 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
20313 RecordTypeToString( type ), rdataStr );
20314 free( rdataStr );
20315 }
20316 _Append( "\n" );
20317
20318 err = DataBuffer_Append( &dataBuf, "", 1 );
20319 require_noerr( err, exit );
20320
20321 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
20322 require_noerr( err, exit );
20323
20324 exit:
20325 DataBuffer_Free( &dataBuf );
20326 return( err );
20327 }
20328
20329 //===========================================================================================================================
20330 // WriteDNSQueryMessage
20331 //===========================================================================================================================
20332
20333 static OSStatus
20334 WriteDNSQueryMessage(
20335 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
20336 uint16_t inMsgID,
20337 uint16_t inFlags,
20338 const char * inQName,
20339 uint16_t inQType,
20340 uint16_t inQClass,
20341 size_t * outMsgLen )
20342 {
20343 OSStatus err;
20344 uint8_t qname[ kDomainNameLengthMax ];
20345
20346 err = DomainNameFromString( qname, inQName, NULL );
20347 require_noerr_quiet( err, exit );
20348
20349 err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
20350 require_noerr_quiet( err, exit );
20351
20352 exit:
20353 return( err );
20354 }
20355
20356 //===========================================================================================================================
20357 // DispatchSignalSourceCreate
20358 //===========================================================================================================================
20359
20360 static OSStatus
20361 DispatchSignalSourceCreate(
20362 int inSignal,
20363 dispatch_queue_t inQueue,
20364 DispatchHandler inEventHandler,
20365 void * inContext,
20366 dispatch_source_t * outSource )
20367 {
20368 OSStatus err;
20369 dispatch_source_t source;
20370
20371 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, inQueue );
20372 require_action( source, exit, err = kUnknownErr );
20373
20374 dispatch_set_context( source, inContext );
20375 dispatch_source_set_event_handler_f( source, inEventHandler );
20376
20377 *outSource = source;
20378 err = kNoErr;
20379
20380 exit:
20381 return( err );
20382 }
20383
20384 //===========================================================================================================================
20385 // DispatchSocketSourceCreate
20386 //===========================================================================================================================
20387
20388 static OSStatus
20389 DispatchSocketSourceCreate(
20390 SocketRef inSock,
20391 dispatch_source_type_t inType,
20392 dispatch_queue_t inQueue,
20393 DispatchHandler inEventHandler,
20394 DispatchHandler inCancelHandler,
20395 void * inContext,
20396 dispatch_source_t * outSource )
20397 {
20398 OSStatus err;
20399 dispatch_source_t source;
20400
20401 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
20402 require_action( source, exit, err = kUnknownErr );
20403
20404 dispatch_set_context( source, inContext );
20405 dispatch_source_set_event_handler_f( source, inEventHandler );
20406 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
20407
20408 *outSource = source;
20409 err = kNoErr;
20410
20411 exit:
20412 return( err );
20413 }
20414
20415 //===========================================================================================================================
20416 // DispatchTimerCreate
20417 //===========================================================================================================================
20418
20419 static OSStatus
20420 DispatchTimerCreate(
20421 dispatch_time_t inStart,
20422 uint64_t inIntervalNs,
20423 uint64_t inLeewayNs,
20424 dispatch_queue_t inQueue,
20425 DispatchHandler inEventHandler,
20426 DispatchHandler inCancelHandler,
20427 void * inContext,
20428 dispatch_source_t * outTimer )
20429 {
20430 OSStatus err;
20431 dispatch_source_t timer;
20432
20433 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
20434 require_action( timer, exit, err = kUnknownErr );
20435
20436 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
20437 dispatch_set_context( timer, inContext );
20438 dispatch_source_set_event_handler_f( timer, inEventHandler );
20439 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
20440
20441 *outTimer = timer;
20442 err = kNoErr;
20443
20444 exit:
20445 return( err );
20446 }
20447
20448 //===========================================================================================================================
20449 // DispatchProcessMonitorCreate
20450 //===========================================================================================================================
20451
20452 static OSStatus
20453 DispatchProcessMonitorCreate(
20454 pid_t inPID,
20455 unsigned long inFlags,
20456 dispatch_queue_t inQueue,
20457 DispatchHandler inEventHandler,
20458 DispatchHandler inCancelHandler,
20459 void * inContext,
20460 dispatch_source_t * outMonitor )
20461 {
20462 OSStatus err;
20463 dispatch_source_t monitor;
20464
20465 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
20466 inQueue ? inQueue : dispatch_get_main_queue() );
20467 require_action( monitor, exit, err = kUnknownErr );
20468
20469 dispatch_set_context( monitor, inContext );
20470 dispatch_source_set_event_handler_f( monitor, inEventHandler );
20471 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
20472
20473 *outMonitor = monitor;
20474 err = kNoErr;
20475
20476 exit:
20477 return( err );
20478 }
20479
20480 //===========================================================================================================================
20481 // ServiceTypeDescription
20482 //===========================================================================================================================
20483
20484 typedef struct
20485 {
20486 const char * name; // Name of the service type in two-label "_service._proto" format.
20487 const char * description; // Description of the service type.
20488
20489 } ServiceType;
20490
20491 // A Non-comprehensive table of DNS-SD service types
20492
20493 static const ServiceType kServiceTypes[] =
20494 {
20495 { "_acp-sync._tcp", "AirPort Base Station Sync" },
20496 { "_adisk._tcp", "Automatic Disk Discovery" },
20497 { "_afpovertcp._tcp", "Apple File Sharing" },
20498 { "_airdrop._tcp", "AirDrop" },
20499 { "_airplay._tcp", "AirPlay" },
20500 { "_airport._tcp", "AirPort Base Station" },
20501 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
20502 { "_eppc._tcp", "Remote AppleEvents" },
20503 { "_ftp._tcp", "File Transfer Protocol" },
20504 { "_home-sharing._tcp", "Home Sharing" },
20505 { "_homekit._tcp", "HomeKit" },
20506 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
20507 { "_https._tcp", "HTTP over SSL/TLS" },
20508 { "_ipp._tcp", "Internet Printing Protocol" },
20509 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
20510 { "_mediaremotetv._tcp", "Media Remote" },
20511 { "_net-assistant._tcp", "Apple Remote Desktop" },
20512 { "_od-master._tcp", "OpenDirectory Master" },
20513 { "_nfs._tcp", "Network File System" },
20514 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
20515 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
20516 { "_raop._tcp", "Remote Audio Output Protocol" },
20517 { "_rfb._tcp", "Remote Frame Buffer" },
20518 { "_scanner._tcp", "Bonjour Scanning" },
20519 { "_smb._tcp", "Server Message Block over TCP/IP" },
20520 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
20521 { "_sleep-proxy._udp", "Sleep Proxy Server" },
20522 { "_ssh._tcp", "SSH Remote Login Protocol" },
20523 { "_teleport._tcp", "teleport" },
20524 { "_tftp._tcp", "Trivial File Transfer Protocol" },
20525 { "_workstation._tcp", "Workgroup Manager" },
20526 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
20527 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
20528 };
20529
20530 static const char * ServiceTypeDescription( const char *inName )
20531 {
20532 const ServiceType * serviceType;
20533 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
20534
20535 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
20536 {
20537 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
20538 {
20539 const char * const ptr = &inName[ strlen( serviceType->name ) ];
20540
20541 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
20542 {
20543 return( serviceType->description );
20544 }
20545 }
20546 }
20547 return( NULL );
20548 }
20549
20550 //===========================================================================================================================
20551 // SocketContextCreate
20552 //===========================================================================================================================
20553
20554 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
20555 {
20556 OSStatus err;
20557 SocketContext * context;
20558
20559 context = (SocketContext *) calloc( 1, sizeof( *context ) );
20560 require_action( context, exit, err = kNoMemoryErr );
20561
20562 context->refCount = 1;
20563 context->sock = inSock;
20564 context->userContext = inUserContext;
20565
20566 *outContext = context;
20567 err = kNoErr;
20568
20569 exit:
20570 return( err );
20571 }
20572
20573 //===========================================================================================================================
20574 // SocketContextRetain
20575 //===========================================================================================================================
20576
20577 static SocketContext * SocketContextRetain( SocketContext *inContext )
20578 {
20579 ++inContext->refCount;
20580 return( inContext );
20581 }
20582
20583 //===========================================================================================================================
20584 // SocketContextRelease
20585 //===========================================================================================================================
20586
20587 static void SocketContextRelease( SocketContext *inContext )
20588 {
20589 if( --inContext->refCount == 0 )
20590 {
20591 ForgetSocket( &inContext->sock );
20592 free( inContext );
20593 }
20594 }
20595
20596 //===========================================================================================================================
20597 // SocketContextCancelHandler
20598 //===========================================================================================================================
20599
20600 static void SocketContextCancelHandler( void *inContext )
20601 {
20602 SocketContextRelease( (SocketContext *) inContext );
20603 }
20604
20605 //===========================================================================================================================
20606 // StringToInt32
20607 //===========================================================================================================================
20608
20609 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
20610 {
20611 OSStatus err;
20612 long value;
20613 char * endPtr;
20614
20615 value = strtol( inString, &endPtr, 0 );
20616 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
20617 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
20618
20619 *outValue = (int32_t) value;
20620 err = kNoErr;
20621
20622 exit:
20623 return( err );
20624 }
20625
20626 //===========================================================================================================================
20627 // StringToUInt32
20628 //===========================================================================================================================
20629
20630 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
20631 {
20632 OSStatus err;
20633 uint32_t value;
20634 char * endPtr;
20635
20636 value = (uint32_t) strtol( inString, &endPtr, 0 );
20637 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
20638
20639 *outValue = value;
20640 err = kNoErr;
20641
20642 exit:
20643 return( err );
20644 }
20645
20646 #if( TARGET_OS_DARWIN )
20647 //===========================================================================================================================
20648 // _StringToInt64
20649 //===========================================================================================================================
20650
20651 static int64_t _StringToInt64( const char *inString, OSStatus *outError )
20652 {
20653 OSStatus err;
20654 long long val;
20655 char * end;
20656 int errnoVal;
20657
20658 set_errno_compat( 0 );
20659 val = strtoll( inString, &end, 0 );
20660 errnoVal = errno_compat();
20661
20662 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
20663 require_action_quiet( ( ( val != LLONG_MIN ) && ( val != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
20664 require_action_quiet( ( val >= INT64_MIN ) && ( val <= INT64_MAX ), exit, err = kRangeErr );
20665 err = kNoErr;
20666
20667 exit:
20668 if( outError ) *outError = err;
20669 return( (int64_t)val );
20670 }
20671
20672 //===========================================================================================================================
20673 // _StringToUInt64
20674 //===========================================================================================================================
20675
20676 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError )
20677 {
20678 OSStatus err;
20679 unsigned long long val;
20680 char * end;
20681 int errnoVal;
20682
20683 set_errno_compat( 0 );
20684 val = strtoull( inString, &end, 0 );
20685 errnoVal = errno_compat();
20686
20687 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
20688 require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
20689 require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
20690 err = kNoErr;
20691
20692 exit:
20693 if( outError ) *outError = err;
20694 return( (uint64_t)val );
20695 }
20696
20697 //===========================================================================================================================
20698 // _StringToPID
20699 //===========================================================================================================================
20700
20701 static pid_t _StringToPID( const char *inString, OSStatus *outError )
20702 {
20703 OSStatus err;
20704 int64_t val;
20705
20706 val = _StringToInt64( inString, &err );
20707 require_noerr_quiet( err, exit );
20708 require_action_quiet( val == (pid_t) val, exit, err = kRangeErr );
20709 err = kNoErr;
20710
20711 exit:
20712 if( outError ) *outError = err;
20713 return( (pid_t) val );
20714 }
20715
20716 //===========================================================================================================================
20717 // _ParseEscapedString
20718 //
20719 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
20720 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
20721 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
20722 //===========================================================================================================================
20723
20724 static OSStatus
20725 _ParseEscapedString(
20726 const char * inSrc,
20727 const char * inEnd,
20728 const char * inDelimiters,
20729 char * inBufPtr,
20730 size_t inBufLen,
20731 size_t * outCopiedLen,
20732 size_t * outActualLen,
20733 const char ** outPtr )
20734 {
20735 OSStatus err;
20736 const char * ptr;
20737 char * dst = inBufPtr;
20738 const char * const lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
20739 size_t len;
20740
20741 len = 0;
20742 ptr = inSrc;
20743 if( !inDelimiters ) inDelimiters = "";
20744 while( ptr < inEnd )
20745 {
20746 int c;
20747 const char * del;
20748
20749 c = *ptr;
20750 for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
20751 if( *del != '\0' ) break;
20752 ++ptr;
20753 if( c == '\\' )
20754 {
20755 require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
20756 c = *ptr++;
20757 }
20758 ++len;
20759 if( dst < lim ) *dst++ = (char) c;
20760 }
20761 if( inBufLen > 0 ) *dst = '\0';
20762
20763 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBufPtr );
20764 if( outActualLen ) *outActualLen = len;
20765 if( outPtr ) *outPtr = ptr;
20766 err = kNoErr;
20767
20768 exit:
20769 return( err );
20770 }
20771 #endif
20772
20773 //===========================================================================================================================
20774 // StringToARecordData
20775 //===========================================================================================================================
20776
20777 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
20778 {
20779 OSStatus err;
20780 uint32_t * addrPtr;
20781 const size_t addrLen = sizeof( *addrPtr );
20782 const char * end;
20783
20784 addrPtr = (uint32_t *) malloc( addrLen );
20785 require_action( addrPtr, exit, err = kNoMemoryErr );
20786
20787 err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
20788 NULL, NULL, NULL, &end );
20789 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
20790 require_noerr_quiet( err, exit );
20791
20792 *addrPtr = HostToBig32( *addrPtr );
20793
20794 *outPtr = (uint8_t *) addrPtr;
20795 addrPtr = NULL;
20796 *outLen = addrLen;
20797
20798 exit:
20799 FreeNullSafe( addrPtr );
20800 return( err );
20801 }
20802
20803 //===========================================================================================================================
20804 // StringToAAAARecordData
20805 //===========================================================================================================================
20806
20807 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
20808 {
20809 OSStatus err;
20810 uint8_t * addrPtr;
20811 const size_t addrLen = 16;
20812 const char * end;
20813
20814 addrPtr = (uint8_t *) malloc( addrLen );
20815 require_action( addrPtr, exit, err = kNoMemoryErr );
20816
20817 err = _StringToIPv6Address( inString,
20818 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
20819 addrPtr, NULL, NULL, NULL, &end );
20820 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
20821 require_noerr_quiet( err, exit );
20822
20823 *outPtr = addrPtr;
20824 addrPtr = NULL;
20825 *outLen = addrLen;
20826
20827 exit:
20828 FreeNullSafe( addrPtr );
20829 return( err );
20830 }
20831
20832 //===========================================================================================================================
20833 // StringToDomainName
20834 //===========================================================================================================================
20835
20836 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
20837 {
20838 OSStatus err;
20839 uint8_t * namePtr;
20840 size_t nameLen;
20841 uint8_t * end;
20842 uint8_t nameBuf[ kDomainNameLengthMax ];
20843
20844 err = DomainNameFromString( nameBuf, inString, &end );
20845 require_noerr_quiet( err, exit );
20846
20847 nameLen = (size_t)( end - nameBuf );
20848 namePtr = _memdup( nameBuf, nameLen );
20849 require_action( namePtr, exit, err = kNoMemoryErr );
20850
20851 *outPtr = namePtr;
20852 namePtr = NULL;
20853 if( outLen ) *outLen = nameLen;
20854
20855 exit:
20856 return( err );
20857 }
20858
20859 #if( TARGET_OS_DARWIN )
20860 //===========================================================================================================================
20861 // GetDefaultDNSServer
20862 //===========================================================================================================================
20863
20864 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
20865 {
20866 OSStatus err;
20867 dns_config_t * config;
20868 struct sockaddr * addr;
20869 int32_t i;
20870
20871 config = dns_configuration_copy();
20872 require_action( config, exit, err = kUnknownErr );
20873
20874 addr = NULL;
20875 for( i = 0; i < config->n_resolver; ++i )
20876 {
20877 const dns_resolver_t * const resolver = config->resolver[ i ];
20878
20879 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
20880 {
20881 addr = resolver->nameserver[ 0 ];
20882 break;
20883 }
20884 }
20885 require_action_quiet( addr, exit, err = kNotFoundErr );
20886
20887 SockAddrCopy( addr, outAddr );
20888 err = kNoErr;
20889
20890 exit:
20891 if( config ) dns_configuration_free( config );
20892 return( err );
20893 }
20894 #endif
20895
20896 //===========================================================================================================================
20897 // GetMDNSMulticastAddrV4
20898 //===========================================================================================================================
20899
20900 static void _MDNSMulticastAddrV4Init( void *inContext );
20901
20902 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
20903 {
20904 static struct sockaddr_in sMDNSMulticastAddrV4;
20905 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
20906
20907 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
20908 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
20909 }
20910
20911 static void _MDNSMulticastAddrV4Init( void *inContext )
20912 {
20913 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
20914
20915 memset( addr, 0, sizeof( *addr ) );
20916 SIN_LEN_SET( addr );
20917 addr->sin_family = AF_INET;
20918 addr->sin_port = htons( kMDNSPort );
20919 addr->sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
20920 }
20921
20922 //===========================================================================================================================
20923 // GetMDNSMulticastAddrV6
20924 //===========================================================================================================================
20925
20926 static void _MDNSMulticastAddrV6Init( void *inContext );
20927
20928 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
20929 {
20930 static struct sockaddr_in6 sMDNSMulticastAddrV6;
20931 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
20932
20933 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
20934 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
20935 }
20936
20937 static void _MDNSMulticastAddrV6Init( void *inContext )
20938 {
20939 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
20940
20941 memset( addr, 0, sizeof( *addr ) );
20942 SIN6_LEN_SET( addr );
20943 addr->sin6_family = AF_INET6;
20944 addr->sin6_port = htons( kMDNSPort );
20945 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
20946 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
20947 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
20948 }
20949
20950 //===========================================================================================================================
20951 // CreateMulticastSocket
20952 //===========================================================================================================================
20953
20954 static OSStatus
20955 CreateMulticastSocket(
20956 const struct sockaddr * inAddr,
20957 int inPort,
20958 const char * inIfName,
20959 uint32_t inIfIndex,
20960 Boolean inJoin,
20961 int * outPort,
20962 SocketRef * outSock )
20963 {
20964 OSStatus err;
20965 SocketRef sock = kInvalidSocketRef;
20966 const int family = inAddr->sa_family;
20967 int port;
20968
20969 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
20970
20971 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
20972 require_noerr_quiet( err, exit );
20973
20974 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
20975 require_noerr_quiet( err, exit );
20976
20977 if( family == AF_INET )
20978 {
20979 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
20980 err = map_socket_noerr_errno( sock, err );
20981 require_noerr_quiet( err, exit );
20982 }
20983 else
20984 {
20985 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
20986 err = map_socket_noerr_errno( sock, err );
20987 require_noerr_quiet( err, exit );
20988 }
20989
20990 if( inJoin )
20991 {
20992 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
20993 require_noerr_quiet( err, exit );
20994 }
20995
20996 if( outPort ) *outPort = port;
20997 *outSock = sock;
20998 sock = kInvalidSocketRef;
20999
21000 exit:
21001 ForgetSocket( &sock );
21002 return( err );
21003 }
21004
21005 //===========================================================================================================================
21006 // DecimalTextToUInt32
21007 //===========================================================================================================================
21008
21009 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
21010 {
21011 OSStatus err;
21012 uint64_t value;
21013 const char * ptr = inSrc;
21014
21015 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
21016
21017 value = (uint64_t)( *ptr++ - '0' );
21018 if( value == 0 )
21019 {
21020 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
21021 {
21022 err = kMalformedErr;
21023 goto exit;
21024 }
21025 }
21026 else
21027 {
21028 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
21029 {
21030 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
21031 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
21032 }
21033 }
21034
21035 *outValue = (uint32_t) value;
21036 if( outPtr ) *outPtr = ptr;
21037 err = kNoErr;
21038
21039 exit:
21040 return( err );
21041 }
21042
21043 //===========================================================================================================================
21044 // CheckIntegerArgument
21045 //===========================================================================================================================
21046
21047 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
21048 {
21049 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
21050
21051 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
21052 return( kRangeErr );
21053 }
21054
21055 //===========================================================================================================================
21056 // CheckDoubleArgument
21057 //===========================================================================================================================
21058
21059 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
21060 {
21061 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
21062
21063 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
21064 return( kRangeErr );
21065 }
21066
21067 //===========================================================================================================================
21068 // CheckRootUser
21069 //===========================================================================================================================
21070
21071 static OSStatus CheckRootUser( void )
21072 {
21073 if( geteuid() == 0 ) return( kNoErr );
21074
21075 FPrintF( stderr, "error: This command must to be run as root.\n" );
21076 return( kPermissionErr );
21077 }
21078
21079 //===========================================================================================================================
21080 // SpawnCommand
21081 //
21082 // Note: Based on systemf() from CoreUtils framework.
21083 //===========================================================================================================================
21084
21085 extern char ** environ;
21086
21087 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... )
21088 {
21089 OSStatus err;
21090 va_list args;
21091 char * command;
21092 char * argv[ 4 ];
21093 pid_t pid;
21094
21095 command = NULL;
21096 va_start( args, inFormat );
21097 VASPrintF( &command, inFormat, args );
21098 va_end( args );
21099 require_action( command, exit, err = kUnknownErr );
21100
21101 argv[ 0 ] = "/bin/sh";
21102 argv[ 1 ] = "-c";
21103 argv[ 2 ] = command;
21104 argv[ 3 ] = NULL;
21105 err = posix_spawn( &pid, argv[ 0 ], NULL, NULL, argv, environ );
21106 free( command );
21107 require_noerr_quiet( err, exit );
21108
21109 if( outPID ) *outPID = pid;
21110
21111 exit:
21112 return( err );
21113 }
21114
21115 //===========================================================================================================================
21116 // OutputFormatFromArgString
21117 //===========================================================================================================================
21118
21119 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
21120 {
21121 OSStatus err;
21122 OutputFormatType format;
21123
21124 format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
21125 kOutputFormatStr_JSON, kOutputFormatType_JSON,
21126 kOutputFormatStr_XML, kOutputFormatType_XML,
21127 kOutputFormatStr_Binary, kOutputFormatType_Binary,
21128 NULL );
21129 if( outFormat ) *outFormat = format;
21130 return( err );
21131 }
21132
21133 //===========================================================================================================================
21134 // OutputPropertyList
21135 //===========================================================================================================================
21136
21137 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
21138 {
21139 OSStatus err;
21140 CFDataRef results = NULL;
21141 FILE * file = NULL;
21142
21143 // Convert plist to a specific format.
21144
21145 switch( inType )
21146 {
21147 case kOutputFormatType_JSON:
21148 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
21149 require_action( results, exit, err = kUnknownErr );
21150 break;
21151
21152 case kOutputFormatType_XML:
21153 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
21154 require_action( results, exit, err = kUnknownErr );
21155 break;
21156
21157 case kOutputFormatType_Binary:
21158 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
21159 require_action( results, exit, err = kUnknownErr );
21160 break;
21161
21162 default:
21163 err = kTypeErr;
21164 goto exit;
21165 }
21166
21167 // Write formatted results to file or stdout.
21168
21169 if( inOutputFilePath )
21170 {
21171 file = fopen( inOutputFilePath, "wb" );
21172 err = map_global_value_errno( file, file );
21173 require_noerr( err, exit );
21174 }
21175 else
21176 {
21177 file = stdout;
21178 }
21179
21180 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
21181 require_noerr_quiet( err, exit );
21182
21183 // Write a trailing newline for JSON-formatted results.
21184
21185 if( inType == kOutputFormatType_JSON )
21186 {
21187 err = WriteANSIFile( file, "\n", 1 );
21188 require_noerr_quiet( err, exit );
21189 }
21190
21191 exit:
21192 if( file && ( file != stdout ) ) fclose( file );
21193 CFReleaseNullSafe( results );
21194 return( err );
21195 }
21196
21197 //===========================================================================================================================
21198 // CreateSRVRecordDataFromString
21199 //===========================================================================================================================
21200
21201 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
21202 {
21203 OSStatus err;
21204 DataBuffer dataBuf;
21205 const char * ptr;
21206 int i;
21207 uint8_t * end;
21208 uint8_t target[ kDomainNameLengthMax ];
21209
21210 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
21211
21212 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
21213
21214 ptr = inString;
21215 for( i = 0; i < 3; ++i )
21216 {
21217 char * next;
21218 long value;
21219 uint8_t buf[ 2 ];
21220
21221 value = strtol( ptr, &next, 0 );
21222 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
21223 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
21224 ptr = next + 1;
21225
21226 WriteBig16( buf, value );
21227
21228 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
21229 require_noerr( err, exit );
21230 }
21231
21232 // Set the target domain name.
21233
21234 err = DomainNameFromString( target, ptr, &end );
21235 require_noerr_quiet( err, exit );
21236
21237 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
21238 require_noerr( err, exit );
21239
21240 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
21241 require_noerr( err, exit );
21242
21243 exit:
21244 DataBuffer_Free( &dataBuf );
21245 return( err );
21246 }
21247
21248 //===========================================================================================================================
21249 // CreateTXTRecordDataFromString
21250 //===========================================================================================================================
21251
21252 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
21253 {
21254 OSStatus err;
21255 DataBuffer dataBuf;
21256 const char * src;
21257 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
21258
21259 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
21260
21261 src = inString;
21262 for( ;; )
21263 {
21264 uint8_t * dst = &txtStr[ 1 ];
21265 const uint8_t * const lim = &txtStr[ 256 ];
21266 int c;
21267
21268 while( *src && ( *src != inDelimiter ) )
21269 {
21270 if( ( c = *src++ ) == '\\' )
21271 {
21272 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
21273 c = *src++;
21274 }
21275 require_action_quiet( dst < lim, exit, err = kOverrunErr );
21276 *dst++ = (uint8_t) c;
21277 }
21278 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
21279 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
21280 require_noerr( err, exit );
21281
21282 if( *src == '\0' ) break;
21283 ++src;
21284 }
21285
21286 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
21287 require_noerr( err, exit );
21288
21289 exit:
21290 DataBuffer_Free( &dataBuf );
21291 return( err );
21292 }
21293
21294 //===========================================================================================================================
21295 // CreateNSECRecordData
21296 //===========================================================================================================================
21297
21298 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
21299 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
21300
21301 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
21302
21303 static OSStatus
21304 CreateNSECRecordData(
21305 const uint8_t * inNextDomainName,
21306 uint8_t ** outPtr,
21307 size_t * outLen,
21308 unsigned int inTypeCount,
21309 ... )
21310 {
21311 OSStatus err;
21312 va_list args;
21313 DataBuffer rdataDB;
21314 unsigned int * array = NULL;
21315 unsigned int i, type, maxBit, currBlock, bitmapLen;
21316 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
21317 uint8_t * const bitmap = &fields[ 2 ];
21318
21319 va_start( args, inTypeCount );
21320 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
21321
21322 // Append Next Domain Name.
21323
21324 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
21325 require_noerr( err, exit );
21326
21327 // Append Type Bit Maps.
21328
21329 maxBit = 0;
21330 memset( bitmap, 0, kNSECBitmapMaxLength );
21331 if( inTypeCount > 0 )
21332 {
21333 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
21334 require_action( array, exit, err = kNoMemoryErr );
21335
21336 for( i = 0; i < inTypeCount; ++i )
21337 {
21338 type = va_arg( args, unsigned int );
21339 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
21340 array[ i ] = type;
21341 }
21342 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
21343
21344 currBlock = array[ 0 ] / 256;
21345 for( i = 0; i < inTypeCount; ++i )
21346 {
21347 const unsigned int block = array[ i ] / 256;
21348 const unsigned int bit = array[ i ] % 256;
21349
21350 if( block != currBlock )
21351 {
21352 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
21353 fields[ 0 ] = (uint8_t) currBlock;
21354 fields[ 1 ] = (uint8_t) bitmapLen;
21355
21356 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
21357 require_noerr( err, exit );
21358
21359 maxBit = 0;
21360 currBlock = block;
21361 memset( bitmap, 0, bitmapLen );
21362 }
21363 BitArray_SetBit( bitmap, bit );
21364 if( bit > maxBit ) maxBit = bit;
21365 }
21366 }
21367 else
21368 {
21369 currBlock = 0;
21370 }
21371
21372 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
21373 fields[ 0 ] = (uint8_t) currBlock;
21374 fields[ 1 ] = (uint8_t) bitmapLen;
21375
21376 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
21377 require_noerr( err, exit );
21378
21379 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
21380 require_noerr( err, exit );
21381
21382 exit:
21383 va_end( args );
21384 DataBuffer_Free( &rdataDB );
21385 FreeNullSafe( array );
21386 return( err );
21387 }
21388
21389 //===========================================================================================================================
21390 // AppendSOARecord
21391 //===========================================================================================================================
21392
21393 static OSStatus
21394 _AppendSOARecordData(
21395 DataBuffer * inDB,
21396 const uint8_t * inMName,
21397 const uint8_t * inRName,
21398 uint32_t inSerial,
21399 uint32_t inRefresh,
21400 uint32_t inRetry,
21401 uint32_t inExpire,
21402 uint32_t inMinimumTTL,
21403 size_t * outLen );
21404
21405 static OSStatus
21406 AppendSOARecord(
21407 DataBuffer * inDB,
21408 const uint8_t * inNamePtr,
21409 size_t inNameLen,
21410 uint16_t inType,
21411 uint16_t inClass,
21412 uint32_t inTTL,
21413 const uint8_t * inMName,
21414 const uint8_t * inRName,
21415 uint32_t inSerial,
21416 uint32_t inRefresh,
21417 uint32_t inRetry,
21418 uint32_t inExpire,
21419 uint32_t inMinimumTTL,
21420 size_t * outLen )
21421 {
21422 OSStatus err;
21423 size_t rdataLen;
21424 size_t rdlengthOffset = 0;
21425 uint8_t * rdlengthPtr;
21426
21427 if( inDB )
21428 {
21429 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
21430 require_noerr( err, exit );
21431
21432 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
21433 }
21434
21435 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
21436 require_noerr( err, exit );
21437
21438 if( inDB )
21439 {
21440 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
21441 WriteBig16( rdlengthPtr, rdataLen );
21442 }
21443
21444 if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
21445 err = kNoErr;
21446
21447 exit:
21448 return( err );
21449 }
21450
21451 static OSStatus
21452 _AppendSOARecordData(
21453 DataBuffer * inDB,
21454 const uint8_t * inMName,
21455 const uint8_t * inRName,
21456 uint32_t inSerial,
21457 uint32_t inRefresh,
21458 uint32_t inRetry,
21459 uint32_t inExpire,
21460 uint32_t inMinimumTTL,
21461 size_t * outLen )
21462 {
21463 OSStatus err;
21464 dns_fixed_fields_soa fields;
21465 const size_t mnameLen = DomainNameLength( inMName );
21466 const size_t rnameLen = DomainNameLength( inRName );
21467
21468 if( inDB )
21469 {
21470 err = DataBuffer_Append( inDB, inMName, mnameLen );
21471 require_noerr( err, exit );
21472
21473 err = DataBuffer_Append( inDB, inRName, rnameLen );
21474 require_noerr( err, exit );
21475
21476 dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
21477 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
21478 require_noerr( err, exit );
21479 }
21480 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
21481 err = kNoErr;
21482
21483 exit:
21484 return( err );
21485 }
21486
21487 //===========================================================================================================================
21488 // CreateSOARecordData
21489 //===========================================================================================================================
21490
21491 static OSStatus
21492 CreateSOARecordData(
21493 const uint8_t * inMName,
21494 const uint8_t * inRName,
21495 uint32_t inSerial,
21496 uint32_t inRefresh,
21497 uint32_t inRetry,
21498 uint32_t inExpire,
21499 uint32_t inMinimumTTL,
21500 uint8_t ** outPtr,
21501 size_t * outLen )
21502 {
21503 OSStatus err;
21504 DataBuffer rdataDB;
21505
21506 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
21507
21508 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
21509 require_noerr( err, exit );
21510
21511 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
21512 require_noerr( err, exit );
21513
21514 exit:
21515 DataBuffer_Free( &rdataDB );
21516 return( err );
21517 }
21518
21519 //===========================================================================================================================
21520 // _DataBuffer_AppendDNSQuestion
21521 //===========================================================================================================================
21522
21523 static OSStatus
21524 _DataBuffer_AppendDNSQuestion(
21525 DataBuffer * inDB,
21526 const uint8_t * inNamePtr,
21527 size_t inNameLen,
21528 uint16_t inType,
21529 uint16_t inClass )
21530 {
21531 OSStatus err;
21532 dns_fixed_fields_question fields;
21533
21534 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
21535 require_noerr( err, exit );
21536
21537 dns_fixed_fields_question_init( &fields, inType, inClass );
21538 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
21539 require_noerr( err, exit );
21540
21541 exit:
21542 return( err );
21543 }
21544
21545 //===========================================================================================================================
21546 // _DataBuffer_AppendDNSRecord
21547 //===========================================================================================================================
21548
21549 static OSStatus
21550 _DataBuffer_AppendDNSRecord(
21551 DataBuffer * inDB,
21552 const uint8_t * inNamePtr,
21553 size_t inNameLen,
21554 uint16_t inType,
21555 uint16_t inClass,
21556 uint32_t inTTL,
21557 const uint8_t * inRDataPtr,
21558 size_t inRDataLen )
21559 {
21560 OSStatus err;
21561 dns_fixed_fields_record fields;
21562
21563 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
21564
21565 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
21566 require_noerr( err, exit );
21567
21568 dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
21569 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
21570 require_noerr( err, exit );
21571
21572 if( inRDataPtr )
21573 {
21574 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
21575 require_noerr( err, exit );
21576 }
21577
21578 exit:
21579 return( err );
21580 }
21581
21582 //===========================================================================================================================
21583 // _NanoTime64ToTimestamp
21584 //===========================================================================================================================
21585
21586 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
21587 {
21588 struct timeval tv;
21589
21590 NanoTimeToTimeVal( inTime, &tv );
21591 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
21592 }
21593
21594 //===========================================================================================================================
21595 // _MDNSInterfaceListCreate
21596 //===========================================================================================================================
21597
21598 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
21599
21600 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
21601 {
21602 OSStatus err;
21603 struct ifaddrs * ifaList;
21604 const struct ifaddrs * ifa;
21605 MDNSInterfaceItem * interfaceList;
21606 MDNSInterfaceItem ** ptr;
21607 SocketRef infoSock;
21608
21609 ifaList = NULL;
21610 interfaceList = NULL;
21611 infoSock = kInvalidSocketRef;
21612 if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
21613 require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
21614
21615 infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
21616 err = map_socket_creation_errno( infoSock );
21617 require_noerr( err, exit );
21618
21619 err = getifaddrs( &ifaList );
21620 err = map_global_noerr_errno( err );
21621 require_noerr( err, exit );
21622
21623 ptr = &interfaceList;
21624 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
21625 {
21626 MDNSInterfaceItem * item;
21627 int family;
21628 const unsigned int flagsMask = IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
21629 const unsigned int flagsNeeded = IFF_UP | IFF_MULTICAST;
21630
21631 if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded ) continue;
21632 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
21633 family = ifa->ifa_addr->sa_family;
21634 if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
21635
21636 for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
21637 if( !item )
21638 {
21639 NetTransportType type;
21640 uint32_t ifIndex;
21641 const char * const ifName = ifa->ifa_name;
21642
21643 if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
21644 err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
21645 require_noerr( err, exit );
21646
21647 if( ifIndex == 0 ) continue;
21648 if( type == kNetTransportType_AWDL )
21649 {
21650 if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
21651 }
21652 else
21653 {
21654 if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
21655 }
21656 item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
21657 require_action( item, exit, err = kNoMemoryErr );
21658
21659 *ptr = item;
21660 ptr = &item->next;
21661
21662 item->ifName = strdup( ifName );
21663 require_action( item->ifName, exit, err = kNoMemoryErr );
21664
21665 item->ifIndex = ifIndex;
21666 if( type == kNetTransportType_AWDL ) item->isAWDL = true;
21667 else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
21668 }
21669 if( family == AF_INET ) item->hasIPv4 = true;
21670 else item->hasIPv6 = true;
21671 }
21672 require_action_quiet( interfaceList, exit, err = kNotFoundErr );
21673
21674 if( outList )
21675 {
21676 *outList = interfaceList;
21677 interfaceList = NULL;
21678 }
21679
21680 exit:
21681 if( ifaList ) freeifaddrs( ifaList );
21682 _MDNSInterfaceListFree( interfaceList );
21683 ForgetSocket( &infoSock );
21684 return( err );
21685 }
21686
21687 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
21688 {
21689 OSStatus err;
21690 int i;
21691 static const char * const kMDNSInterfacePrefixBlacklist[] = { "llw" };
21692 struct ifreq ifr;
21693
21694 // Check if the interface name's prefix matches the prefix blacklist.
21695
21696 for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
21697 {
21698 const char * const prefix = kMDNSInterfacePrefixBlacklist[ i ];
21699
21700 if( strcmp_prefix( inIfName, prefix ) == 0 )
21701 {
21702 const char * ptr = &inIfName[ strlen( prefix ) ];
21703
21704 while( isdigit_safe( *ptr ) ) ++ptr;
21705 if( *ptr == '\0' ) return( true );
21706 }
21707 }
21708
21709 // Check if the interface is used for inter-(co)processor networking.
21710
21711 memset( &ifr, 0, sizeof( ifr ) );
21712 strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
21713 err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
21714 err = map_global_value_errno( err != -1, err );
21715 if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
21716
21717 return( false );
21718 }
21719
21720 //===========================================================================================================================
21721 // _MDNSInterfaceListFree
21722 //===========================================================================================================================
21723
21724 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList )
21725 {
21726 MDNSInterfaceItem * item;
21727
21728 while( ( item = inList ) != NULL )
21729 {
21730 inList = item->next;
21731 FreeNullSafe( item->ifName );
21732 free( item );
21733 }
21734 }
21735
21736 //===========================================================================================================================
21737 // _MDNSInterfaceGetAny
21738 //===========================================================================================================================
21739
21740 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
21741 {
21742 OSStatus err;
21743 MDNSInterfaceItem * list;
21744 const MDNSInterfaceItem * item;
21745
21746 list = NULL;
21747 err = _MDNSInterfaceListCreate( inSubset, 0, &list );
21748 require_noerr_quiet( err, exit );
21749 require_action_quiet( list, exit, err = kNotFoundErr );
21750
21751 for( item = list; item; item = item->next )
21752 {
21753 if( item->hasIPv4 && item->hasIPv6 ) break;
21754 }
21755 if( !item ) item = list;
21756 if( inNameBuf ) strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
21757 if( outIndex ) *outIndex = item->ifIndex;
21758
21759 exit:
21760 _MDNSInterfaceListFree( list );
21761 return( err );
21762 }
21763
21764 //===========================================================================================================================
21765 // _SetComputerName
21766 //===========================================================================================================================
21767
21768 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
21769 {
21770 OSStatus err;
21771 SCPreferencesRef prefs;
21772 Boolean ok;
21773
21774 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
21775 err = map_scerror( prefs );
21776 require_noerr_quiet( err, exit );
21777
21778 ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
21779 err = map_scerror( ok );
21780 require_noerr_quiet( err, exit );
21781
21782 ok = SCPreferencesCommitChanges( prefs );
21783 err = map_scerror( ok );
21784 require_noerr_quiet( err, exit );
21785
21786 ok = SCPreferencesApplyChanges( prefs );
21787 err = map_scerror( ok );
21788 require_noerr_quiet( err, exit );
21789
21790 exit:
21791 CFReleaseNullSafe( prefs );
21792 return( err );
21793 }
21794
21795 //===========================================================================================================================
21796 // _SetComputerNameWithUTF8CString
21797 //===========================================================================================================================
21798
21799 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName )
21800 {
21801 OSStatus err;
21802 CFStringRef computerName;
21803
21804 computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
21805 require_action( computerName, exit, err = kNoMemoryErr );
21806
21807 err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
21808 require_noerr_quiet( err, exit );
21809
21810 exit:
21811 CFReleaseNullSafe( computerName );
21812 return( err );
21813 }
21814
21815 //===========================================================================================================================
21816 // _SetLocalHostName
21817 //===========================================================================================================================
21818
21819 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName )
21820 {
21821 OSStatus err;
21822 SCPreferencesRef prefs;
21823 Boolean ok;
21824
21825 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
21826 err = map_scerror( prefs );
21827 require_noerr_quiet( err, exit );
21828
21829 ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
21830 err = map_scerror( ok );
21831 require_noerr_quiet( err, exit );
21832
21833 ok = SCPreferencesCommitChanges( prefs );
21834 err = map_scerror( ok );
21835 require_noerr_quiet( err, exit );
21836
21837 ok = SCPreferencesApplyChanges( prefs );
21838 err = map_scerror( ok );
21839 require_noerr_quiet( err, exit );
21840
21841 exit:
21842 CFReleaseNullSafe( prefs );
21843 return( err );
21844 }
21845
21846 //===========================================================================================================================
21847 // _SetLocalHostNameWithUTF8CString
21848 //===========================================================================================================================
21849
21850 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
21851 {
21852 OSStatus err;
21853 CFStringRef localHostName;
21854
21855 localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
21856 require_action( localHostName, exit, err = kNoMemoryErr );
21857
21858 err = _SetLocalHostName( localHostName );
21859 require_noerr_quiet( err, exit );
21860
21861 exit:
21862 CFReleaseNullSafe( localHostName );
21863 return( err );
21864 }
21865
21866 //===========================================================================================================================
21867 // MDNSColliderCreate
21868 //===========================================================================================================================
21869
21870 typedef enum
21871 {
21872 kMDNSColliderOpCode_Invalid = 0,
21873 kMDNSColliderOpCode_Send = 1,
21874 kMDNSColliderOpCode_Wait = 2,
21875 kMDNSColliderOpCode_SetProbeActions = 3,
21876 kMDNSColliderOpCode_LoopPush = 4,
21877 kMDNSColliderOpCode_LoopPop = 5,
21878 kMDNSColliderOpCode_Exit = 6
21879
21880 } MDNSColliderOpCode;
21881
21882 typedef struct
21883 {
21884 MDNSColliderOpCode opcode;
21885 uint32_t operand;
21886
21887 } MDNSCInstruction;
21888
21889 #define kMaxLoopDepth 16
21890
21891 struct MDNSColliderPrivate
21892 {
21893 CFRuntimeBase base; // CF object base.
21894 dispatch_queue_t queue; // Queue for collider's events.
21895 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
21896 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
21897 SocketRef sockV4; // IPv4 UDP socket for mDNS.
21898 SocketRef sockV6; // IPv6 UDP socket for mDNS.
21899 uint8_t * target; // Record name being targeted. (malloced)
21900 uint8_t * responsePtr; // Response message pointer. (malloced)
21901 size_t responseLen; // Response message length.
21902 uint8_t * probePtr; // Probe query message pointer. (malloced)
21903 size_t probeLen; // Probe query message length.
21904 unsigned int probeCount; // Count of probe queries received for collider's record.
21905 uint32_t probeActionMap; // Bitmap of actions to take for
21906 MDNSCInstruction * program; // Program to execute.
21907 uint32_t pc; // Program's program counter.
21908 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
21909 uint32_t loopDepth; // Current loop depth.
21910 dispatch_source_t waitTimer; // Timer for program's wait commands.
21911 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
21912 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
21913 void * stopContext; // User's stop handler context.
21914 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
21915 Boolean stopped; // True if the collider has been stopped.
21916 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
21917 };
21918
21919 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
21920 static void _MDNSColliderReadHandler( void *inContext );
21921 static void _MDNSColliderExecuteProgram( void *inContext );
21922 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
21923 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
21924
21925 CF_CLASS_DEFINE( MDNSCollider );
21926
21927 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
21928 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
21929
21930 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
21931 {
21932 OSStatus err;
21933 MDNSColliderRef obj = NULL;
21934
21935 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
21936
21937 ReplaceDispatchQueue( &obj->queue, inQueue );
21938 obj->sockV4 = kInvalidSocketRef;
21939 obj->sockV6 = kInvalidSocketRef;
21940
21941 *outCollider = obj;
21942 err = kNoErr;
21943
21944 exit:
21945 return( err );
21946 }
21947
21948 //===========================================================================================================================
21949 // _MDNSColliderFinalize
21950 //===========================================================================================================================
21951
21952 static void _MDNSColliderFinalize( CFTypeRef inObj )
21953 {
21954 MDNSColliderRef const me = (MDNSColliderRef) inObj;
21955
21956 check( !me->waitTimer );
21957 check( !me->readSourceV4 );
21958 check( !me->readSourceV6 );
21959 check( !IsValidSocket( me->sockV4 ) );
21960 check( !IsValidSocket( me->sockV6 ) );
21961 ForgetMem( &me->target );
21962 ForgetMem( &me->responsePtr );
21963 ForgetMem( &me->probePtr );
21964 ForgetMem( &me->program );
21965 dispatch_forget( &me->queue );
21966 }
21967
21968 //===========================================================================================================================
21969 // MDNSColliderStart
21970 //===========================================================================================================================
21971
21972 static void _MDNSColliderStart( void *inContext );
21973
21974 static OSStatus MDNSColliderStart( MDNSColliderRef me )
21975 {
21976 OSStatus err;
21977
21978 require_action_quiet( me->target, exit, err = kNotPreparedErr );
21979 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
21980 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
21981 require_action_quiet( me->program, exit, err = kNotPreparedErr );
21982 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
21983 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
21984
21985 CFRetain( me );
21986 dispatch_async_f( me->queue, me, _MDNSColliderStart );
21987 err = kNoErr;
21988
21989 exit:
21990 return( err );
21991 }
21992
21993 static void _MDNSColliderStart( void *inContext )
21994 {
21995 OSStatus err;
21996 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21997 SocketRef sock = kInvalidSocketRef;
21998 SocketContext * sockCtx = NULL;
21999
22000 if( me->protocols & kMDNSColliderProtocol_IPv4 )
22001 {
22002 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
22003 require_noerr( err, exit );
22004
22005 err = SocketContextCreate( sock, me, &sockCtx );
22006 require_noerr( err, exit );
22007 sock = kInvalidSocketRef;
22008
22009 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
22010 sockCtx, &me->readSourceV4 );
22011 require_noerr( err, exit );
22012 me->sockV4 = sockCtx->sock;
22013 sockCtx = NULL;
22014
22015 dispatch_resume( me->readSourceV4 );
22016 }
22017
22018 if( me->protocols & kMDNSColliderProtocol_IPv6 )
22019 {
22020 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
22021 require_noerr( err, exit );
22022
22023 err = SocketContextCreate( sock, me, &sockCtx );
22024 require_noerr( err, exit );
22025 sock = kInvalidSocketRef;
22026
22027 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
22028 sockCtx, &me->readSourceV6 );
22029 require_noerr( err, exit );
22030 me->sockV6 = sockCtx->sock;
22031 sockCtx = NULL;
22032
22033 dispatch_resume( me->readSourceV6 );
22034 }
22035
22036 _MDNSColliderExecuteProgram( me );
22037 err = kNoErr;
22038
22039 exit:
22040 ForgetSocket( &sock );
22041 ForgetSocketContext( &sockCtx );
22042 if( err ) _MDNSColliderStop( me, err );
22043 }
22044
22045 //===========================================================================================================================
22046 // MDNSColliderStop
22047 //===========================================================================================================================
22048
22049 static void _MDNSColliderUserStop( void *inContext );
22050
22051 static void MDNSColliderStop( MDNSColliderRef me )
22052 {
22053 CFRetain( me );
22054 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
22055 }
22056
22057 static void _MDNSColliderUserStop( void *inContext )
22058 {
22059 MDNSColliderRef const me = (MDNSColliderRef) inContext;
22060
22061 _MDNSColliderStop( me, kCanceledErr );
22062 CFRelease( me );
22063 }
22064
22065 //===========================================================================================================================
22066 // MDNSColliderSetProtocols
22067 //===========================================================================================================================
22068
22069 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
22070 {
22071 me->protocols = inProtocols;
22072 }
22073
22074 //===========================================================================================================================
22075 // MDNSColliderSetInterfaceIndex
22076 //===========================================================================================================================
22077
22078 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
22079 {
22080 me->interfaceIndex = inInterfaceIndex;
22081 }
22082
22083 //===========================================================================================================================
22084 // MDNSColliderSetProgram
22085 //===========================================================================================================================
22086
22087 #define kMDNSColliderProgCmd_Done "done"
22088 #define kMDNSColliderProgCmd_Loop "loop"
22089 #define kMDNSColliderProgCmd_Send "send"
22090 #define kMDNSColliderProgCmd_Probes "probes"
22091 #define kMDNSColliderProgCmd_Wait "wait"
22092
22093 typedef uint32_t MDNSColliderProbeAction;
22094
22095 #define kMDNSColliderProbeAction_None 0
22096 #define kMDNSColliderProbeAction_Respond 1
22097 #define kMDNSColliderProbeAction_RespondUnicast 2
22098 #define kMDNSColliderProbeAction_RespondMulticast 3
22099 #define kMDNSColliderProbeAction_Probe 4
22100 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
22101
22102 #define kMDNSColliderProbeActionBits_Count 3
22103 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
22104 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
22105
22106 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
22107
22108 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
22109
22110 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
22111 {
22112 OSStatus err;
22113 uint32_t insCount;
22114 unsigned int loopDepth;
22115 const char * cmd;
22116 const char * end;
22117 const char * next;
22118 MDNSCInstruction * program = NULL;
22119 uint32_t loopStart[ kMaxLoopDepth ];
22120
22121 insCount = 0;
22122 for( cmd = inProgramStr; *cmd; cmd = next )
22123 {
22124 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
22125 require_action_quiet( end != cmd, exit, err = kMalformedErr );
22126 next = ( *end == ';' ) ? ( end + 1 ) : end;
22127 ++insCount;
22128 }
22129
22130 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
22131 require_action( program, exit, err = kNoMemoryErr );
22132
22133 insCount = 0;
22134 loopDepth = 0;
22135 for( cmd = inProgramStr; *cmd; cmd = next )
22136 {
22137 size_t cmdLen;
22138 const char * ptr;
22139 const char * arg;
22140 size_t argLen;
22141 uint32_t value;
22142 MDNSCInstruction * const ins = &program[ insCount ];
22143
22144 while( isspace_safe( *cmd ) ) ++cmd;
22145 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
22146 next = ( *end == ';' ) ? ( end + 1 ) : end;
22147
22148 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
22149 cmdLen = (size_t)( ptr - cmd );
22150
22151 // Done statement
22152
22153 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
22154 {
22155 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
22156 require_action_quiet( ptr == end, exit, err = kMalformedErr );
22157
22158 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
22159
22160 ins->opcode = kMDNSColliderOpCode_LoopPop;
22161 ins->operand = loopStart[ --loopDepth ];
22162 }
22163
22164 // Loop command
22165
22166 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
22167 {
22168 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
22169 err = DecimalTextToUInt32( arg, end, &value, &ptr );
22170 require_noerr_quiet( err, exit );
22171 require_action_quiet( value > 0, exit, err = kValueErr );
22172
22173 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
22174 require_action_quiet( ptr == end, exit, err = kMalformedErr );
22175
22176 ins->opcode = kMDNSColliderOpCode_LoopPush;
22177 ins->operand = value;
22178
22179 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
22180 loopStart[ loopDepth++ ] = insCount + 1;
22181 }
22182
22183 // Probes command
22184
22185 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
22186 {
22187 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
22188 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
22189 argLen = (size_t)( ptr - arg );
22190 if( argLen > 0 )
22191 {
22192 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
22193 require_noerr_quiet( err, exit );
22194 }
22195 else
22196 {
22197 value = 0;
22198 }
22199
22200 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
22201 require_action_quiet( ptr == end, exit, err = kMalformedErr );
22202
22203 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
22204 ins->operand = value;
22205 }
22206
22207 // Send command
22208
22209 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
22210 {
22211 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
22212 require_action_quiet( ptr == end, exit, err = kMalformedErr );
22213
22214 ins->opcode = kMDNSColliderOpCode_Send;
22215 }
22216
22217 // Wait command
22218
22219 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
22220 {
22221 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
22222 err = DecimalTextToUInt32( arg, end, &value, &ptr );
22223 require_noerr_quiet( err, exit );
22224
22225 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
22226 require_action_quiet( ptr == end, exit, err = kMalformedErr );
22227
22228 ins->opcode = kMDNSColliderOpCode_Wait;
22229 ins->operand = value;
22230 }
22231
22232 // Unrecognized command
22233
22234 else
22235 {
22236 err = kCommandErr;
22237 goto exit;
22238 }
22239 ++insCount;
22240 }
22241 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
22242
22243 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
22244
22245 FreeNullSafe( me->program );
22246 me->program = program;
22247 program = NULL;
22248 err = kNoErr;
22249
22250 exit:
22251 FreeNullSafe( program );
22252 return( err );
22253 }
22254
22255 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
22256 {
22257 OSStatus err;
22258 const char * ptr;
22259 const char * const end = &inString[ inLen ];
22260 uint32_t bitmap;
22261 int index;
22262
22263 bitmap = 0;
22264 index = 0;
22265 ptr = inString;
22266 while( ptr < end )
22267 {
22268 int c, count;
22269 MDNSColliderProbeAction action;
22270
22271 c = *ptr++;
22272 if( isdigit_safe( c ) )
22273 {
22274 count = 0;
22275 do
22276 {
22277 count = ( count * 10 ) + ( c - '0' );
22278 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
22279 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
22280 c = *ptr++;
22281
22282 } while( isdigit_safe( c ) );
22283 require_action_quiet( count > 0, exit, err = kCountErr );
22284 }
22285 else
22286 {
22287 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
22288 count = 1;
22289 }
22290
22291 switch( c )
22292 {
22293 case 'n': action = kMDNSColliderProbeAction_None; break;
22294 case 'r': action = kMDNSColliderProbeAction_Respond; break;
22295 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
22296 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
22297 case 'p': action = kMDNSColliderProbeAction_Probe; break;
22298 default: err = kMalformedErr; goto exit;
22299 }
22300 if( ptr < end )
22301 {
22302 c = *ptr++;
22303 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
22304 }
22305 while( count-- > 0 )
22306 {
22307 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
22308 ++index;
22309 }
22310 }
22311
22312 *outBitmap = bitmap;
22313 err = kNoErr;
22314
22315 exit:
22316 return( err );
22317 }
22318
22319 //===========================================================================================================================
22320 // MDNSColliderSetStopHandler
22321 //===========================================================================================================================
22322
22323 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
22324 {
22325 me->stopHandler = inStopHandler;
22326 me->stopContext = inStopContext;
22327 }
22328
22329 //===========================================================================================================================
22330 // MDNSColliderSetRecord
22331 //===========================================================================================================================
22332
22333 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
22334 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
22335 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
22336
22337 static OSStatus
22338 MDNSColliderSetRecord(
22339 MDNSColliderRef me,
22340 const uint8_t * inName,
22341 uint16_t inType,
22342 const void * inRDataPtr,
22343 size_t inRDataLen )
22344 {
22345 OSStatus err;
22346 DataBuffer msgDB;
22347 DNSHeader header;
22348 uint8_t * targetPtr = NULL;
22349 size_t targetLen;
22350 uint8_t * responsePtr = NULL;
22351 size_t responseLen;
22352 uint8_t * probePtr = NULL;
22353 size_t probeLen;
22354
22355 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
22356
22357 err = DomainNameDup( inName, &targetPtr, &targetLen );
22358 require_noerr_quiet( err, exit );
22359
22360 // Create response message.
22361
22362 memset( &header, 0, sizeof( header ) );
22363 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
22364 DNSHeaderSetAnswerCount( &header, 1 );
22365
22366 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
22367 require_noerr( err, exit );
22368
22369 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kRRClassCacheFlushBit,
22370 1976, inRDataPtr, inRDataLen );
22371 require_noerr( err, exit );
22372
22373 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
22374 require_noerr( err, exit );
22375
22376 // Create probe message.
22377
22378 memset( &header, 0, sizeof( header ) );
22379 DNSHeaderSetQuestionCount( &header, 2 );
22380 DNSHeaderSetAuthorityCount( &header, 1 );
22381
22382 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
22383 require_noerr( err, exit );
22384
22385 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
22386 require_noerr( err, exit );
22387
22388 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
22389 kDNSServiceType_NULL, kDNSServiceClass_IN );
22390 require_noerr( err, exit );
22391
22392 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
22393 1976, inRDataPtr, inRDataLen );
22394 require_noerr( err, exit );
22395
22396 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
22397 require_noerr( err, exit );
22398
22399 FreeNullSafe( me->target );
22400 me->target = targetPtr;
22401 targetPtr = NULL;
22402
22403 FreeNullSafe( me->responsePtr );
22404 me->responsePtr = responsePtr;
22405 me->responseLen = responseLen;
22406 responsePtr = NULL;
22407
22408 FreeNullSafe( me->probePtr );
22409 me->probePtr = probePtr;
22410 me->probeLen = probeLen;
22411 probePtr = NULL;
22412
22413 exit:
22414 DataBuffer_Free( &msgDB );
22415 FreeNullSafe( targetPtr );
22416 FreeNullSafe( responsePtr );
22417 FreeNullSafe( probePtr );
22418 return( err );
22419 }
22420
22421 //===========================================================================================================================
22422 // _MDNSColliderStop
22423 //===========================================================================================================================
22424
22425 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
22426 {
22427 dispatch_source_forget( &me->waitTimer );
22428 dispatch_source_forget( &me->readSourceV4 );
22429 dispatch_source_forget( &me->readSourceV6 );
22430 me->sockV4 = kInvalidSocketRef;
22431 me->sockV6 = kInvalidSocketRef;
22432
22433 if( !me->stopped )
22434 {
22435 me->stopped = true;
22436 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
22437 CFRelease( me );
22438 }
22439 }
22440
22441 //===========================================================================================================================
22442 // _MDNSColliderReadHandler
22443 //===========================================================================================================================
22444
22445 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
22446 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
22447
22448 static void _MDNSColliderReadHandler( void *inContext )
22449 {
22450 OSStatus err;
22451 struct timeval now;
22452 SocketContext * const sockCtx = (SocketContext *) inContext;
22453 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
22454 size_t msgLen;
22455 sockaddr_ip sender;
22456 const DNSHeader * hdr;
22457 const uint8_t * ptr;
22458 const struct sockaddr * dest;
22459 int probeFound, probeIsQU;
22460 unsigned int qCount, i;
22461 MDNSColliderProbeAction action;
22462
22463 gettimeofday( &now, NULL );
22464
22465 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
22466 NULL, NULL, NULL, NULL );
22467 require_noerr( err, exit );
22468
22469 require_quiet( msgLen >= kDNSHeaderLength, exit );
22470 hdr = (const DNSHeader *) me->msgBuf;
22471
22472 probeFound = false;
22473 probeIsQU = false;
22474 qCount = DNSHeaderGetQuestionCount( hdr );
22475 ptr = (const uint8_t *) &hdr[ 1 ];
22476 for( i = 0; i < qCount; ++i )
22477 {
22478 uint16_t qtype, qclass;
22479 uint8_t qname[ kDomainNameLengthMax ];
22480
22481 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
22482 require_noerr_quiet( err, exit );
22483
22484 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
22485 DomainNameEqual( qname, kMDNSColliderDummyName ) )
22486 {
22487 probeFound = false;
22488 break;
22489 }
22490
22491 if( qtype != kDNSServiceType_ANY ) continue;
22492 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
22493 if( !DomainNameEqual( qname, me->target ) ) continue;
22494
22495 if( !probeFound )
22496 {
22497 probeFound = true;
22498 probeIsQU = ( qclass & kQClassUnicastResponseBit ) ? true : false;
22499 }
22500 }
22501 require_quiet( probeFound, exit );
22502
22503 ++me->probeCount;
22504 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
22505
22506 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
22507 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
22508
22509 if( ( action == kMDNSColliderProbeAction_Respond ) ||
22510 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
22511 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
22512 {
22513 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
22514 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
22515 {
22516 dest = &sender.sa;
22517 }
22518 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
22519 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
22520 {
22521 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
22522 }
22523
22524 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
22525 require_noerr( err, exit );
22526 }
22527 else if( action == kMDNSColliderProbeAction_Probe )
22528 {
22529 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
22530
22531 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
22532 require_noerr( err, exit );
22533 }
22534
22535 exit:
22536 return;
22537 }
22538
22539 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
22540 {
22541 MDNSColliderProbeAction action;
22542
22543 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
22544 {
22545 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
22546 kMDNSColliderProbeActionBits_Mask;
22547 }
22548 else
22549 {
22550 action = kMDNSColliderProbeAction_None;
22551 }
22552 return( action );
22553 }
22554
22555 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
22556 {
22557 switch( inAction )
22558 {
22559 case kMDNSColliderProbeAction_None: return( "None" );
22560 case kMDNSColliderProbeAction_Respond: return( "Respond" );
22561 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
22562 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
22563 case kMDNSColliderProbeAction_Probe: return( "Probe" );
22564 default: return( "???" );
22565 }
22566 }
22567
22568 //===========================================================================================================================
22569 // _MDNSColliderExecuteProgram
22570 //===========================================================================================================================
22571
22572 static void _MDNSColliderExecuteProgram( void *inContext )
22573 {
22574 OSStatus err;
22575 MDNSColliderRef const me = (MDNSColliderRef) inContext;
22576 int stop;
22577
22578 dispatch_forget( &me->waitTimer );
22579
22580 stop = false;
22581 for( ;; )
22582 {
22583 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
22584 uint32_t waitMs;
22585
22586 switch( ins->opcode )
22587 {
22588 case kMDNSColliderOpCode_Send:
22589 if( IsValidSocket( me->sockV4 ) )
22590 {
22591 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
22592 require_noerr( err, exit );
22593 }
22594 if( IsValidSocket( me->sockV6 ) )
22595 {
22596 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
22597 require_noerr( err, exit );
22598 }
22599 break;
22600
22601 case kMDNSColliderOpCode_Wait:
22602 waitMs = ins->operand;
22603 if( waitMs > 0 )
22604 {
22605 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
22606 _MDNSColliderExecuteProgram, me, &me->waitTimer );
22607 require_noerr( err, exit );
22608 dispatch_resume( me->waitTimer );
22609 goto exit;
22610 }
22611 break;
22612
22613 case kMDNSColliderOpCode_SetProbeActions:
22614 me->probeCount = 0;
22615 me->probeActionMap = ins->operand;
22616 break;
22617
22618 case kMDNSColliderOpCode_LoopPush:
22619 check( me->loopDepth < kMaxLoopDepth );
22620 me->loopCounts[ me->loopDepth++ ] = ins->operand;
22621 break;
22622
22623 case kMDNSColliderOpCode_LoopPop:
22624 check( me->loopDepth > 0 );
22625 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
22626 {
22627 me->pc = ins->operand;
22628 }
22629 else
22630 {
22631 --me->loopDepth;
22632 }
22633 break;
22634
22635 case kMDNSColliderOpCode_Exit:
22636 stop = true;
22637 err = kNoErr;
22638 goto exit;
22639
22640 default:
22641 dlogassert( "Unhandled opcode %u\n", ins->opcode );
22642 err = kCommandErr;
22643 goto exit;
22644 }
22645 }
22646
22647 exit:
22648 if( err || stop ) _MDNSColliderStop( me, err );
22649 }
22650
22651 //===========================================================================================================================
22652 // _MDNSColliderSendResponse
22653 //===========================================================================================================================
22654
22655 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
22656 {
22657 OSStatus err;
22658 ssize_t n;
22659
22660 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
22661 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
22662 return( err );
22663 }
22664
22665 //===========================================================================================================================
22666 // _MDNSColliderSendProbe
22667 //===========================================================================================================================
22668
22669 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
22670 {
22671 OSStatus err;
22672 ssize_t n;
22673
22674 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
22675 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
22676 return( err );
22677 }
22678
22679 //===========================================================================================================================
22680 // ServiceBrowserCreate
22681 //===========================================================================================================================
22682
22683 typedef struct SBDomain SBDomain;
22684 typedef struct SBServiceType SBServiceType;
22685 typedef struct SBServiceBrowse SBServiceBrowse;
22686 typedef struct SBServiceInstance SBServiceInstance;
22687 typedef struct SBIPAddress SBIPAddress;
22688
22689 struct ServiceBrowserPrivate
22690 {
22691 CFRuntimeBase base; // CF object base.
22692 dispatch_queue_t queue; // Queue for service browser's events.
22693 DNSServiceRef connection; // Shared connection for DNS-SD ops.
22694 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
22695 char * domain; // If non-null, then browsing is limited to this domain.
22696 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
22697 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
22698 void * userContext; // User's callback context.
22699 SBDomain * domainList; // List of domains and their browse results.
22700 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
22701 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
22702 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
22703 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
22704 // use the "any" interface.
22705 };
22706
22707 struct SBDomain
22708 {
22709 SBDomain * next; // Next domain object in list.
22710 ServiceBrowserRef browser; // Pointer to parent service browser.
22711 char * name; // Name of the domain.
22712 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
22713 SBServiceType * typeList; // List of service types to browse for in this domain.
22714 };
22715
22716 struct SBServiceType
22717 {
22718 SBServiceType * next; // Next service type object in list.
22719 char * name; // Name of the service type.
22720 SBServiceBrowse * browseList; // List of browses for this service type.
22721 };
22722
22723 struct SBServiceBrowse
22724 {
22725 SBServiceBrowse * next; // Next browse object in list.
22726 ServiceBrowserRef browser; // Pointer to parent service browser.
22727 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
22728 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
22729 uint64_t startTicks; // Value of UpTicks() when the browse op began.
22730 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
22731 };
22732
22733 struct SBServiceInstance
22734 {
22735 SBServiceInstance * next; // Next service instance object in list.
22736 ServiceBrowserRef browser; // Pointer to parent service browser.
22737 char * name; // Name of the service instance.
22738 char * fqdn; // Fully qualified domain name of service instance (for logging/debugging).
22739 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
22740 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
22741 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
22742 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
22743 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
22744 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
22745 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
22746 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
22747 size_t txtLen; // Length of service instance's TXT record data.
22748 DNSServiceRef getAddrInfo; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
22749 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
22750 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
22751 };
22752
22753 struct SBIPAddress
22754 {
22755 SBIPAddress * next; // Next IP address object in list.
22756 sockaddr_ip sip; // IPv4 or IPv6 address.
22757 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
22758 };
22759
22760 typedef struct
22761 {
22762 SBRDomain * domainList; // List of domains in which services were found.
22763 int32_t refCount; // This object's reference count.
22764
22765 } ServiceBrowserResultsPrivate;
22766
22767 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
22768 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
22769 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
22770 static void _ServiceBrowserTimerHandler( void *inContext );
22771 static void DNSSD_API
22772 _ServiceBrowserDomainsQueryCallback(
22773 DNSServiceRef inSDRef,
22774 DNSServiceFlags inFlags,
22775 uint32_t inInterfaceIndex,
22776 DNSServiceErrorType inError,
22777 const char * inFullName,
22778 uint16_t inType,
22779 uint16_t inClass,
22780 uint16_t inRDataLen,
22781 const void * inRDataPtr,
22782 uint32_t inTTL,
22783 void * inContext );
22784 static void DNSSD_API
22785 _ServiceBrowserServicesQueryCallback(
22786 DNSServiceRef inSDRef,
22787 DNSServiceFlags inFlags,
22788 uint32_t inInterfaceIndex,
22789 DNSServiceErrorType inError,
22790 const char * inFullName,
22791 uint16_t inType,
22792 uint16_t inClass,
22793 uint16_t inRDataLen,
22794 const void * inRDataPtr,
22795 uint32_t inTTL,
22796 void * inContext );
22797 static void DNSSD_API
22798 _ServiceBrowserBrowseCallback(
22799 DNSServiceRef inSDRef,
22800 DNSServiceFlags inFlags,
22801 uint32_t inInterfaceIndex,
22802 DNSServiceErrorType inError,
22803 const char * inName,
22804 const char * inRegType,
22805 const char * inDomain,
22806 void * inContext );
22807 static void DNSSD_API
22808 _ServiceBrowserResolveCallback(
22809 DNSServiceRef inSDRef,
22810 DNSServiceFlags inFlags,
22811 uint32_t inInterfaceIndex,
22812 DNSServiceErrorType inError,
22813 const char * inFullName,
22814 const char * inHostname,
22815 uint16_t inPort,
22816 uint16_t inTXTLen,
22817 const unsigned char * inTXTPtr,
22818 void * inContext );
22819 static void DNSSD_API
22820 _ServiceBrowserGAICallback(
22821 DNSServiceRef inSDRef,
22822 DNSServiceFlags inFlags,
22823 uint32_t inInterfaceIndex,
22824 DNSServiceErrorType inError,
22825 const char * inHostname,
22826 const struct sockaddr * inSockAddr,
22827 uint32_t inTTL,
22828 void * inContext );
22829 static OSStatus
22830 _ServiceBrowserAddServiceType(
22831 ServiceBrowserRef inBrowser,
22832 SBDomain * inDomain,
22833 const char * inName,
22834 uint32_t inIfIndex );
22835 static OSStatus
22836 _ServiceBrowserRemoveServiceType(
22837 ServiceBrowserRef inBrowser,
22838 SBDomain * inDomain,
22839 const char * inName,
22840 uint32_t inIfIndex );
22841 static OSStatus
22842 _ServiceBrowserAddServiceInstance(
22843 ServiceBrowserRef inBrowser,
22844 SBServiceBrowse * inBrowse,
22845 uint32_t inIfIndex,
22846 const char * inName,
22847 const char * inRegType,
22848 const char * inDomain,
22849 uint64_t inDiscoverTimeUs );
22850 static OSStatus
22851 _ServiceBrowserRemoveServiceInstance(
22852 ServiceBrowserRef inBrowser,
22853 SBServiceBrowse * inBrowse,
22854 const char * inName,
22855 uint32_t inIfIndex );
22856 static OSStatus
22857 _ServiceBrowserAddIPAddress(
22858 ServiceBrowserRef inBrowser,
22859 SBServiceInstance * inInstance,
22860 const struct sockaddr * inSockAddr,
22861 uint64_t inResolveTimeUs );
22862 static OSStatus
22863 _ServiceBrowserRemoveIPAddress(
22864 ServiceBrowserRef inBrowser,
22865 SBServiceInstance * inInstance,
22866 const struct sockaddr * inSockAddr );
22867 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
22868 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
22869 static void _SBDomainFree( SBDomain *inDomain );
22870 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
22871 static void _SBServiceTypeFree( SBServiceType *inType );
22872 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
22873 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
22874 static OSStatus
22875 _SBServiceInstanceCreate(
22876 const char * inName,
22877 const char * inType,
22878 const char * inDomain,
22879 uint32_t inIfIndex,
22880 uint64_t inDiscoverTimeUs,
22881 ServiceBrowserRef inBrowser,
22882 SBServiceInstance ** outInstance );
22883 static void _SBServiceInstanceFree( SBServiceInstance *inInstance );
22884 static OSStatus
22885 _SBIPAddressCreate(
22886 const struct sockaddr * inSockAddr,
22887 uint64_t inResolveTimeUs,
22888 SBIPAddress ** outIPAddress );
22889 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
22890 static void _SBIPAddressFreeList( SBIPAddress *inList );
22891 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
22892 static void _SBRDomainFree( SBRDomain *inDomain );
22893 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
22894 static void _SBRServiceTypeFree( SBRServiceType *inType );
22895 static OSStatus
22896 _SBRServiceInstanceCreate(
22897 const char * inName,
22898 uint32_t inInterfaceIndex,
22899 const char * inHostname,
22900 uint16_t inPort,
22901 const uint8_t * inTXTPtr,
22902 size_t inTXTLen,
22903 uint64_t inDiscoverTimeUs,
22904 uint64_t inResolveTimeUs,
22905 SBRServiceInstance ** outInstance );
22906 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
22907 static OSStatus
22908 _SBRIPAddressCreate(
22909 const struct sockaddr * inSockAddr,
22910 uint64_t inResolveTimeUs,
22911 SBRIPAddress ** outIPAddress );
22912 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
22913
22914 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
22915
22916 CF_CLASS_DEFINE( ServiceBrowser );
22917
22918 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
22919 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
22920
22921 static OSStatus
22922 ServiceBrowserCreate(
22923 dispatch_queue_t inQueue,
22924 uint32_t inInterfaceIndex,
22925 const char * inDomain,
22926 unsigned int inBrowseTimeSecs,
22927 Boolean inIncludeAWDL,
22928 ServiceBrowserRef * outBrowser )
22929 {
22930 OSStatus err;
22931 ServiceBrowserRef obj;
22932
22933 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
22934
22935 ReplaceDispatchQueue( &obj->queue, inQueue );
22936 obj->ifIndex = inInterfaceIndex;
22937 if( inDomain )
22938 {
22939 obj->domain = strdup( inDomain );
22940 require_action( obj->domain, exit, err = kNoMemoryErr );
22941 }
22942 obj->browseTimeSecs = inBrowseTimeSecs;
22943 obj->includeAWDL = inIncludeAWDL;
22944
22945 *outBrowser = obj;
22946 obj = NULL;
22947 err = kNoErr;
22948
22949 exit:
22950 CFReleaseNullSafe( obj );
22951 return( err );
22952 }
22953
22954 //===========================================================================================================================
22955 // _ServiceBrowserFinalize
22956 //===========================================================================================================================
22957
22958 static void _ServiceBrowserFinalize( CFTypeRef inObj )
22959 {
22960 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
22961 StringListItem * serviceType;
22962
22963 dispatch_forget( &me->queue );
22964 check( !me->connection );
22965 check( !me->domainsQuery );
22966 ForgetMem( &me->domain );
22967 while( ( serviceType = me->serviceTypeList ) != NULL )
22968 {
22969 me->serviceTypeList = serviceType->next;
22970 ForgetMem( &serviceType->str );
22971 free( serviceType );
22972 }
22973 check( !me->domainList );
22974 check( !me->stopTimer );
22975 }
22976
22977 //===========================================================================================================================
22978 // ServiceBrowserStart
22979 //===========================================================================================================================
22980
22981 static void _ServiceBrowserStart( void *inContext );
22982
22983 static void ServiceBrowserStart( ServiceBrowserRef me )
22984 {
22985 CFRetain( me );
22986 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
22987 }
22988
22989 static void _ServiceBrowserStart( void *inContext )
22990 {
22991 OSStatus err;
22992 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22993
22994 err = DNSServiceCreateConnection( &me->connection );
22995 require_noerr( err, exit );
22996
22997 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
22998 require_noerr( err, exit );
22999
23000 if( me->domain )
23001 {
23002 err = _ServiceBrowserAddDomain( me, me->domain );
23003 require_noerr( err, exit );
23004 }
23005 else
23006 {
23007 DNSServiceRef sdRef;
23008 const char * const recordName = "b._dns-sd._udp.local.";
23009 const uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
23010
23011 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
23012 // See <https://tools.ietf.org/html/rfc6763#section-11>.
23013
23014 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
23015
23016 sdRef = me->connection;
23017 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
23018 kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
23019 require_noerr( err, exit );
23020
23021 me->domainsQuery = sdRef;
23022 }
23023
23024 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
23025 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
23026 require_noerr( err, exit );
23027 dispatch_resume( me->stopTimer );
23028
23029 exit:
23030 if( err ) _ServiceBrowserStop( me, err );
23031 }
23032
23033 //===========================================================================================================================
23034 // ServiceBrowserAddServiceType
23035 //===========================================================================================================================
23036
23037 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
23038 {
23039 OSStatus err;
23040 StringListItem * item;
23041 StringListItem ** itemPtr;
23042 StringListItem * newItem = NULL;
23043
23044 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
23045 {
23046 if( strcmp( item->str, inServiceType ) == 0 ) break;
23047 }
23048 if( !item )
23049 {
23050 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
23051 require_action( newItem, exit, err = kNoMemoryErr );
23052
23053 newItem->str = strdup( inServiceType );
23054 require_action( newItem->str, exit, err = kNoMemoryErr );
23055
23056 *itemPtr = newItem;
23057 newItem = NULL;
23058 }
23059 err = kNoErr;
23060
23061 exit:
23062 FreeNullSafe( newItem );
23063 return( err );
23064 }
23065
23066 //===========================================================================================================================
23067 // ServiceBrowserSetCallback
23068 //===========================================================================================================================
23069
23070 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
23071 {
23072 me->userCallback = inCallback;
23073 me->userContext = inContext;
23074 }
23075
23076 //===========================================================================================================================
23077 // ServiceBrowserResultsRetain
23078 //===========================================================================================================================
23079
23080 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
23081 {
23082 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
23083
23084 atomic_add_32( &results->refCount, 1 );
23085 }
23086
23087 //===========================================================================================================================
23088 // ServiceBrowserResultsRelease
23089 //===========================================================================================================================
23090
23091 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
23092 {
23093 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
23094 SBRDomain * domain;
23095
23096 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
23097 {
23098 while( ( domain = inResults->domainList ) != NULL )
23099 {
23100 inResults->domainList = domain->next;
23101 _SBRDomainFree( domain );
23102 }
23103 free( inResults );
23104 }
23105 }
23106
23107 //===========================================================================================================================
23108 // _ServiceBrowserStop
23109 //===========================================================================================================================
23110
23111 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
23112 {
23113 OSStatus err;
23114 SBDomain * d;
23115 SBServiceType * t;
23116 SBServiceBrowse * b;
23117 SBServiceInstance * i;
23118
23119 dispatch_source_forget( &me->stopTimer );
23120 DNSServiceForget( &me->domainsQuery );
23121 for( d = me->domainList; d; d = d->next )
23122 {
23123 DNSServiceForget( &d->servicesQuery );
23124 for( t = d->typeList; t; t = t->next )
23125 {
23126 for( b = t->browseList; b; b = b->next )
23127 {
23128 DNSServiceForget( &b->browse );
23129 for( i = b->instanceList; i; i = i->next )
23130 {
23131 DNSServiceForget( &i->resolve );
23132 DNSServiceForget( &i->getAddrInfo );
23133 }
23134 }
23135 }
23136 }
23137 DNSServiceForget( &me->connection );
23138
23139 if( me->userCallback )
23140 {
23141 ServiceBrowserResults * results = NULL;
23142
23143 err = _ServiceBrowserCreateResults( me, &results );
23144 if( !err ) err = inError;
23145
23146 me->userCallback( results, err, me->userContext );
23147 me->userCallback = NULL;
23148 me->userContext = NULL;
23149 if( results ) ServiceBrowserResultsRelease( results );
23150 }
23151
23152 while( ( d = me->domainList ) != NULL )
23153 {
23154 me->domainList = d->next;
23155 _SBDomainFree( d );
23156 }
23157 CFRelease( me );
23158 }
23159
23160 //===========================================================================================================================
23161 // _ServiceBrowserAddDomain
23162 //===========================================================================================================================
23163
23164 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
23165 {
23166 OSStatus err;
23167 SBDomain * domain;
23168 SBDomain ** domainPtr;
23169 SBDomain * newDomain = NULL;
23170
23171 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
23172 {
23173 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
23174 }
23175 require_action_quiet( !domain, exit, err = kDuplicateErr );
23176
23177 err = _SBDomainCreate( inDomain, me, &newDomain );
23178 require_noerr_quiet( err, exit );
23179
23180 if( me->serviceTypeList )
23181 {
23182 const StringListItem * item;
23183
23184 for( item = me->serviceTypeList; item; item = item->next )
23185 {
23186 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
23187 if( err == kDuplicateErr ) err = kNoErr;
23188 require_noerr( err, exit );
23189 }
23190 }
23191 else
23192 {
23193 char * recordName;
23194 DNSServiceRef sdRef;
23195 DNSServiceFlags flags;
23196
23197 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
23198 // See <https://tools.ietf.org/html/rfc6763#section-9>.
23199
23200 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
23201 require_action( recordName, exit, err = kNoMemoryErr );
23202
23203 flags = kDNSServiceFlagsShareConnection;
23204 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
23205
23206 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
23207
23208 sdRef = newDomain->browser->connection;
23209 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
23210 _ServiceBrowserServicesQueryCallback, newDomain );
23211 free( recordName );
23212 require_noerr( err, exit );
23213
23214 newDomain->servicesQuery = sdRef;
23215 }
23216
23217 *domainPtr = newDomain;
23218 newDomain = NULL;
23219 err = kNoErr;
23220
23221 exit:
23222 if( newDomain ) _SBDomainFree( newDomain );
23223 return( err );
23224 }
23225
23226 //===========================================================================================================================
23227 // _ServiceBrowserRemoveDomain
23228 //===========================================================================================================================
23229
23230 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
23231 {
23232 OSStatus err;
23233 SBDomain * domain;
23234 SBDomain ** domainPtr;
23235
23236 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
23237 {
23238 if( strcasecmp( domain->name, inName ) == 0 ) break;
23239 }
23240
23241 if( domain )
23242 {
23243 *domainPtr = domain->next;
23244 _SBDomainFree( domain );
23245 err = kNoErr;
23246 }
23247 else
23248 {
23249 err = kNotFoundErr;
23250 }
23251
23252 return( err );
23253 }
23254
23255 //===========================================================================================================================
23256 // _ServiceBrowserTimerHandler
23257 //===========================================================================================================================
23258
23259 static void _ServiceBrowserTimerHandler( void *inContext )
23260 {
23261 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
23262
23263 _ServiceBrowserStop( me, kNoErr );
23264 }
23265
23266 //===========================================================================================================================
23267 // _ServiceBrowserDomainsQueryCallback
23268 //===========================================================================================================================
23269
23270 static void DNSSD_API
23271 _ServiceBrowserDomainsQueryCallback(
23272 DNSServiceRef inSDRef,
23273 DNSServiceFlags inFlags,
23274 uint32_t inInterfaceIndex,
23275 DNSServiceErrorType inError,
23276 const char * inFullName,
23277 uint16_t inType,
23278 uint16_t inClass,
23279 uint16_t inRDataLen,
23280 const void * inRDataPtr,
23281 uint32_t inTTL,
23282 void * inContext )
23283 {
23284 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
23285 OSStatus err;
23286 char domainStr[ kDNSServiceMaxDomainName ];
23287
23288 Unused( inSDRef );
23289 Unused( inClass );
23290 Unused( inTTL );
23291
23292 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
23293 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
23294 !inError, inError );
23295
23296 require_noerr( inError, exit );
23297
23298 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
23299 require_noerr( err, exit );
23300
23301 if( inFlags & kDNSServiceFlagsAdd )
23302 {
23303 err = _ServiceBrowserAddDomain( me, domainStr );
23304 if( err == kDuplicateErr ) err = kNoErr;
23305 require_noerr( err, exit );
23306 }
23307 else
23308 {
23309 err = _ServiceBrowserRemoveDomain( me, domainStr );
23310 if( err == kNotFoundErr ) err = kNoErr;
23311 require_noerr( err, exit );
23312 }
23313
23314 exit:
23315 return;
23316 }
23317
23318 //===========================================================================================================================
23319 // _ServiceBrowserServicesQueryCallback
23320 //===========================================================================================================================
23321
23322 static void DNSSD_API
23323 _ServiceBrowserServicesQueryCallback(
23324 DNSServiceRef inSDRef,
23325 DNSServiceFlags inFlags,
23326 uint32_t inInterfaceIndex,
23327 DNSServiceErrorType inError,
23328 const char * inFullName,
23329 uint16_t inType,
23330 uint16_t inClass,
23331 uint16_t inRDataLen,
23332 const void * inRDataPtr,
23333 uint32_t inTTL,
23334 void * inContext )
23335 {
23336 OSStatus err;
23337 SBDomain * const domain = (SBDomain *) inContext;
23338 ServiceBrowserRef const me = domain->browser;
23339 const uint8_t * src;
23340 const uint8_t * end;
23341 uint8_t * dst;
23342 int i;
23343 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
23344 char serviceTypeStr[ kDNSServiceMaxDomainName ];
23345
23346 Unused( inSDRef );
23347 Unused( inTTL );
23348 Unused( inClass );
23349
23350 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
23351 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
23352 !inError, inError );
23353
23354 require_noerr( inError, exit );
23355
23356 check( inType == kDNSServiceType_PTR );
23357 check( inClass == kDNSServiceClass_IN );
23358
23359 // The first two labels of the domain name in the RDATA describe a service type.
23360 // See <https://tools.ietf.org/html/rfc6763#section-9>.
23361
23362 src = (const uint8_t *) inRDataPtr;
23363 end = src + inRDataLen;
23364 dst = serviceType;
23365 for( i = 0; i < 2; ++i )
23366 {
23367 size_t labelLen;
23368
23369 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
23370
23371 labelLen = *src;
23372 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
23373 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
23374
23375 memcpy( dst, src, 1 + labelLen );
23376 src += 1 + labelLen;
23377 dst += 1 + labelLen;
23378 }
23379 *dst = 0;
23380
23381 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
23382 require_noerr( err, exit );
23383
23384 if( inFlags & kDNSServiceFlagsAdd )
23385 {
23386 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
23387 if( err == kDuplicateErr ) err = kNoErr;
23388 require_noerr( err, exit );
23389 }
23390 else
23391 {
23392 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
23393 if( err == kNotFoundErr ) err = kNoErr;
23394 require_noerr( err, exit );
23395 }
23396
23397 exit:
23398 return;
23399 }
23400
23401 //===========================================================================================================================
23402 // _ServiceBrowserBrowseCallback
23403 //===========================================================================================================================
23404
23405 static void DNSSD_API
23406 _ServiceBrowserBrowseCallback(
23407 DNSServiceRef inSDRef,
23408 DNSServiceFlags inFlags,
23409 uint32_t inInterfaceIndex,
23410 DNSServiceErrorType inError,
23411 const char * inName,
23412 const char * inRegType,
23413 const char * inDomain,
23414 void * inContext )
23415 {
23416 OSStatus err;
23417 const uint64_t nowTicks = UpTicks();
23418 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
23419 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
23420
23421 Unused( inSDRef );
23422
23423 sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
23424 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
23425
23426 require_noerr( inError, exit );
23427
23428 if( inFlags & kDNSServiceFlagsAdd )
23429 {
23430 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
23431 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
23432 if( err == kDuplicateErr ) err = kNoErr;
23433 require_noerr( err, exit );
23434 }
23435 else
23436 {
23437 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
23438 if( err == kNotFoundErr ) err = kNoErr;
23439 require_noerr( err, exit );
23440 }
23441
23442 exit:
23443 return;
23444 }
23445
23446 //===========================================================================================================================
23447 // _ServiceBrowserResolveCallback
23448 //===========================================================================================================================
23449
23450 static void DNSSD_API
23451 _ServiceBrowserResolveCallback(
23452 DNSServiceRef inSDRef,
23453 DNSServiceFlags inFlags,
23454 uint32_t inInterfaceIndex,
23455 DNSServiceErrorType inError,
23456 const char * inFullName,
23457 const char * inHostname,
23458 uint16_t inPort,
23459 uint16_t inTXTLen,
23460 const unsigned char * inTXTPtr,
23461 void * inContext )
23462 {
23463 OSStatus err;
23464 const uint64_t nowTicks = UpTicks();
23465 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
23466 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
23467
23468 Unused( inSDRef );
23469 Unused( inFlags );
23470
23471 sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
23472 (int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
23473
23474 require_noerr( inError, exit );
23475
23476 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
23477 {
23478 FreeNullSafe( instance->txtPtr );
23479 instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
23480 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
23481
23482 instance->txtLen = inTXTLen;
23483 }
23484
23485 instance->port = ntohs( inPort );
23486
23487 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
23488 {
23489 DNSServiceRef sdRef;
23490
23491 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
23492
23493 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
23494 require_noerr( err, exit );
23495
23496 DNSServiceForget( &instance->getAddrInfo );
23497 ForgetSBIPAddressList( &instance->ipaddrList );
23498
23499 sb_ulog( kLogLevelTrace, "Starting GetAddrInfo on interface %d for %s",
23500 (int32_t) instance->ifIndex, instance->hostname );
23501
23502 sdRef = me->connection;
23503 instance->gaiStartTicks = UpTicks();
23504 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
23505 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback, instance );
23506 require_noerr( err, exit );
23507
23508 instance->getAddrInfo = sdRef;
23509 }
23510
23511 exit:
23512 return;
23513 }
23514
23515 //===========================================================================================================================
23516 // _ServiceBrowserGAICallback
23517 //===========================================================================================================================
23518
23519 static void DNSSD_API
23520 _ServiceBrowserGAICallback(
23521 DNSServiceRef inSDRef,
23522 DNSServiceFlags inFlags,
23523 uint32_t inInterfaceIndex,
23524 DNSServiceErrorType inError,
23525 const char * inHostname,
23526 const struct sockaddr * inSockAddr,
23527 uint32_t inTTL,
23528 void * inContext )
23529 {
23530 OSStatus err;
23531 const uint64_t nowTicks = UpTicks();
23532 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
23533 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
23534
23535 Unused( inSDRef );
23536 Unused( inTTL );
23537
23538 sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
23539 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
23540 !inError, inError );
23541
23542 require_noerr( inError, exit );
23543
23544 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
23545 {
23546 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
23547 goto exit;
23548 }
23549
23550 if( inFlags & kDNSServiceFlagsAdd )
23551 {
23552 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
23553 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
23554 if( err == kDuplicateErr ) err = kNoErr;
23555 require_noerr( err, exit );
23556 }
23557 else
23558 {
23559 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
23560 if( err == kNotFoundErr ) err = kNoErr;
23561 require_noerr( err, exit );
23562 }
23563
23564 exit:
23565 return;
23566 }
23567
23568 //===========================================================================================================================
23569 // _ServiceBrowserAddServiceType
23570 //===========================================================================================================================
23571
23572 static OSStatus
23573 _ServiceBrowserAddServiceType(
23574 ServiceBrowserRef me,
23575 SBDomain * inDomain,
23576 const char * inName,
23577 uint32_t inIfIndex )
23578 {
23579 OSStatus err;
23580 SBServiceType * type;
23581 SBServiceType ** typePtr;
23582 SBServiceType * newType = NULL;
23583 SBServiceBrowse * browse;
23584 SBServiceBrowse ** browsePtr;
23585 SBServiceBrowse * newBrowse = NULL;
23586 DNSServiceRef sdRef;
23587 DNSServiceFlags flags;
23588
23589 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
23590 {
23591 if( strcasecmp( type->name, inName ) == 0 ) break;
23592 }
23593 if( !type )
23594 {
23595 err = _SBServiceTypeCreate( inName, &newType );
23596 require_noerr_quiet( err, exit );
23597
23598 type = newType;
23599 }
23600
23601 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
23602 {
23603 if( browse->ifIndex == inIfIndex ) break;
23604 }
23605 require_action_quiet( !browse, exit, err = kDuplicateErr );
23606
23607 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
23608 require_noerr_quiet( err, exit );
23609
23610 flags = kDNSServiceFlagsShareConnection;
23611 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
23612
23613 sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
23614 (int32_t) newBrowse->ifIndex, type->name, inDomain->name );
23615
23616 sdRef = me->connection;
23617 newBrowse->startTicks = UpTicks();
23618 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
23619 newBrowse );
23620 require_noerr( err, exit );
23621
23622 newBrowse->browse = sdRef;
23623 *browsePtr = newBrowse;
23624 newBrowse = NULL;
23625
23626 if( newType )
23627 {
23628 *typePtr = newType;
23629 newType = NULL;
23630 }
23631
23632 exit:
23633 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
23634 if( newType ) _SBServiceTypeFree( newType );
23635 return( err );
23636 }
23637
23638 //===========================================================================================================================
23639 // _ServiceBrowserRemoveServiceType
23640 //===========================================================================================================================
23641
23642 static OSStatus
23643 _ServiceBrowserRemoveServiceType(
23644 ServiceBrowserRef me,
23645 SBDomain * inDomain,
23646 const char * inName,
23647 uint32_t inIfIndex )
23648 {
23649 OSStatus err;
23650 SBServiceType * type;
23651 SBServiceType ** typePtr;
23652 SBServiceBrowse * browse;
23653 SBServiceBrowse ** browsePtr;
23654
23655 Unused( me );
23656
23657 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
23658 {
23659 if( strcasecmp( type->name, inName ) == 0 ) break;
23660 }
23661 require_action_quiet( type, exit, err = kNotFoundErr );
23662
23663 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
23664 {
23665 if( browse->ifIndex == inIfIndex ) break;
23666 }
23667 require_action_quiet( browse, exit, err = kNotFoundErr );
23668
23669 *browsePtr = browse->next;
23670 _SBServiceBrowseFree( browse );
23671 if( !type->browseList )
23672 {
23673 *typePtr = type->next;
23674 _SBServiceTypeFree( type );
23675 }
23676 err = kNoErr;
23677
23678 exit:
23679 return( err );
23680 }
23681
23682 //===========================================================================================================================
23683 // _ServiceBrowserAddServiceInstance
23684 //===========================================================================================================================
23685
23686 static OSStatus
23687 _ServiceBrowserAddServiceInstance(
23688 ServiceBrowserRef me,
23689 SBServiceBrowse * inBrowse,
23690 uint32_t inIfIndex,
23691 const char * inName,
23692 const char * inRegType,
23693 const char * inDomain,
23694 uint64_t inDiscoverTimeUs )
23695 {
23696 OSStatus err;
23697 DNSServiceRef sdRef;
23698 SBServiceInstance * instance;
23699 SBServiceInstance ** instancePtr;
23700 SBServiceInstance * newInstance = NULL;
23701
23702 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
23703 {
23704 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
23705 }
23706 require_action_quiet( !instance, exit, err = kDuplicateErr );
23707
23708 err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
23709 require_noerr_quiet( err, exit );
23710
23711 sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
23712 (int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
23713
23714 sdRef = me->connection;
23715 newInstance->resolveStartTicks = UpTicks();
23716 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
23717 _ServiceBrowserResolveCallback, newInstance );
23718 require_noerr( err, exit );
23719
23720 newInstance->resolve = sdRef;
23721 *instancePtr = newInstance;
23722 newInstance = NULL;
23723
23724 exit:
23725 if( newInstance ) _SBServiceInstanceFree( newInstance );
23726 return( err );
23727 }
23728
23729 //===========================================================================================================================
23730 // _ServiceBrowserRemoveServiceInstance
23731 //===========================================================================================================================
23732
23733 static OSStatus
23734 _ServiceBrowserRemoveServiceInstance(
23735 ServiceBrowserRef me,
23736 SBServiceBrowse * inBrowse,
23737 const char * inName,
23738 uint32_t inIfIndex )
23739 {
23740 OSStatus err;
23741 SBServiceInstance * instance;
23742 SBServiceInstance ** ptr;
23743
23744 Unused( me );
23745
23746 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
23747 {
23748 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
23749 }
23750 require_action_quiet( instance, exit, err = kNotFoundErr );
23751
23752 *ptr = instance->next;
23753 _SBServiceInstanceFree( instance );
23754 err = kNoErr;
23755
23756 exit:
23757 return( err );
23758 }
23759
23760 //===========================================================================================================================
23761 // _ServiceBrowserAddIPAddress
23762 //===========================================================================================================================
23763
23764 static OSStatus
23765 _ServiceBrowserAddIPAddress(
23766 ServiceBrowserRef me,
23767 SBServiceInstance * inInstance,
23768 const struct sockaddr * inSockAddr,
23769 uint64_t inResolveTimeUs )
23770 {
23771 OSStatus err;
23772 SBIPAddress * ipaddr;
23773 SBIPAddress ** ipaddrPtr;
23774 SBIPAddress * newIPAddr = NULL;
23775
23776 Unused( me );
23777
23778 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
23779 {
23780 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
23781 err = kTypeErr;
23782 goto exit;
23783 }
23784
23785 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
23786 {
23787 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
23788 }
23789 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
23790
23791 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
23792 require_noerr_quiet( err, exit );
23793
23794 *ipaddrPtr = newIPAddr;
23795 newIPAddr = NULL;
23796 err = kNoErr;
23797
23798 exit:
23799 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
23800 return( err );
23801 }
23802
23803 //===========================================================================================================================
23804 // _ServiceBrowserRemoveIPAddress
23805 //===========================================================================================================================
23806
23807 static OSStatus
23808 _ServiceBrowserRemoveIPAddress(
23809 ServiceBrowserRef me,
23810 SBServiceInstance * inInstance,
23811 const struct sockaddr * inSockAddr )
23812 {
23813 OSStatus err;
23814 SBIPAddress * ipaddr;
23815 SBIPAddress ** ipaddrPtr;
23816
23817 Unused( me );
23818
23819 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
23820 {
23821 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
23822 }
23823 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
23824
23825 *ipaddrPtr = ipaddr->next;
23826 _SBIPAddressFree( ipaddr );
23827 err = kNoErr;
23828
23829 exit:
23830 return( err );
23831 }
23832
23833 //===========================================================================================================================
23834 // _ServiceBrowserCreateResults
23835 //===========================================================================================================================
23836
23837 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
23838 {
23839 OSStatus err;
23840 SBDomain * d;
23841 SBServiceType * t;
23842 SBServiceBrowse * b;
23843 SBServiceInstance * i;
23844 SBIPAddress * a;
23845 ServiceBrowserResultsPrivate * results;
23846 SBRDomain ** domainPtr;
23847
23848 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
23849 require_action( results, exit, err = kNoMemoryErr );
23850
23851 results->refCount = 1;
23852
23853 domainPtr = &results->domainList;
23854 for( d = me->domainList; d; d = d->next )
23855 {
23856 SBRDomain * domain;
23857 SBRServiceType ** typePtr;
23858
23859 err = _SBRDomainCreate( d->name, &domain );
23860 require_noerr_quiet( err, exit );
23861 *domainPtr = domain;
23862 domainPtr = &domain->next;
23863
23864 typePtr = &domain->typeList;
23865 for( t = d->typeList; t; t = t->next )
23866 {
23867 SBRServiceType * type;
23868 SBRServiceInstance ** instancePtr;
23869
23870 err = _SBRServiceTypeCreate( t->name, &type );
23871 require_noerr_quiet( err, exit );
23872 *typePtr = type;
23873 typePtr = &type->next;
23874
23875 instancePtr = &type->instanceList;
23876 for( b = t->browseList; b; b = b->next )
23877 {
23878 for( i = b->instanceList; i; i = i->next )
23879 {
23880 SBRServiceInstance * instance;
23881 SBRIPAddress ** ipaddrPtr;
23882
23883 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
23884 i->discoverTimeUs, i->resolveTimeUs, &instance );
23885 require_noerr_quiet( err, exit );
23886 *instancePtr = instance;
23887 instancePtr = &instance->next;
23888
23889 ipaddrPtr = &instance->ipaddrList;
23890 for( a = i->ipaddrList; a; a = a->next )
23891 {
23892 SBRIPAddress * ipaddr;
23893
23894 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
23895 require_noerr_quiet( err, exit );
23896
23897 *ipaddrPtr = ipaddr;
23898 ipaddrPtr = &ipaddr->next;
23899 }
23900 }
23901 }
23902 }
23903 }
23904
23905 *outResults = (ServiceBrowserResults *) results;
23906 results = NULL;
23907 err = kNoErr;
23908
23909 exit:
23910 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
23911 return( err );
23912 }
23913
23914 //===========================================================================================================================
23915 // _SBDomainCreate
23916 //===========================================================================================================================
23917
23918 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
23919 {
23920 OSStatus err;
23921 SBDomain * obj;
23922
23923 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
23924 require_action( obj, exit, err = kNoMemoryErr );
23925
23926 obj->name = strdup( inName );
23927 require_action( obj->name, exit, err = kNoMemoryErr );
23928
23929 obj->browser = inBrowser;
23930
23931 *outDomain = obj;
23932 obj = NULL;
23933 err = kNoErr;
23934
23935 exit:
23936 if( obj ) _SBDomainFree( obj );
23937 return( err );
23938 }
23939
23940 //===========================================================================================================================
23941 // _SBDomainFree
23942 //===========================================================================================================================
23943
23944 static void _SBDomainFree( SBDomain *inDomain )
23945 {
23946 SBServiceType * type;
23947
23948 ForgetMem( &inDomain->name );
23949 DNSServiceForget( &inDomain->servicesQuery );
23950 while( ( type = inDomain->typeList ) != NULL )
23951 {
23952 inDomain->typeList = type->next;
23953 _SBServiceTypeFree( type );
23954 }
23955 free( inDomain );
23956 }
23957
23958 //===========================================================================================================================
23959 // _SBServiceTypeCreate
23960 //===========================================================================================================================
23961
23962 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
23963 {
23964 OSStatus err;
23965 SBServiceType * obj;
23966
23967 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
23968 require_action( obj, exit, err = kNoMemoryErr );
23969
23970 obj->name = strdup( inName );
23971 require_action( obj->name, exit, err = kNoMemoryErr );
23972
23973 *outType = obj;
23974 obj = NULL;
23975 err = kNoErr;
23976
23977 exit:
23978 if( obj ) _SBServiceTypeFree( obj );
23979 return( err );
23980 }
23981
23982 //===========================================================================================================================
23983 // _SBServiceTypeFree
23984 //===========================================================================================================================
23985
23986 static void _SBServiceTypeFree( SBServiceType *inType )
23987 {
23988 SBServiceBrowse * browse;
23989
23990 ForgetMem( &inType->name );
23991 while( ( browse = inType->browseList ) != NULL )
23992 {
23993 inType->browseList = browse->next;
23994 _SBServiceBrowseFree( browse );
23995 }
23996 free( inType );
23997 }
23998
23999 //===========================================================================================================================
24000 // _SBServiceBrowseCreate
24001 //===========================================================================================================================
24002
24003 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
24004 {
24005 OSStatus err;
24006 SBServiceBrowse * obj;
24007
24008 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
24009 require_action( obj, exit, err = kNoMemoryErr );
24010
24011 obj->ifIndex = inIfIndex;
24012 obj->browser = inBrowser;
24013 *outBrowse = obj;
24014 err = kNoErr;
24015
24016 exit:
24017 return( err );
24018 }
24019
24020 //===========================================================================================================================
24021 // _SBServiceBrowseFree
24022 //===========================================================================================================================
24023
24024 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
24025 {
24026 SBServiceInstance * instance;
24027
24028 DNSServiceForget( &inBrowse->browse );
24029 while( ( instance = inBrowse->instanceList ) != NULL )
24030 {
24031 inBrowse->instanceList = instance->next;
24032 _SBServiceInstanceFree( instance );
24033 }
24034 free( inBrowse );
24035 }
24036
24037 //===========================================================================================================================
24038 // _SBServiceInstanceCreate
24039 //===========================================================================================================================
24040
24041 static OSStatus
24042 _SBServiceInstanceCreate(
24043 const char * inName,
24044 const char * inType,
24045 const char * inDomain,
24046 uint32_t inIfIndex,
24047 uint64_t inDiscoverTimeUs,
24048 ServiceBrowserRef inBrowser,
24049 SBServiceInstance ** outInstance )
24050 {
24051 OSStatus err;
24052 SBServiceInstance * obj;
24053
24054 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
24055 require_action( obj, exit, err = kNoMemoryErr );
24056
24057 obj->name = strdup( inName );
24058 require_action( obj->name, exit, err = kNoMemoryErr );
24059
24060 ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
24061 require_action( obj->fqdn, exit, err = kNoMemoryErr );
24062
24063 obj->ifIndex = inIfIndex;
24064 obj->discoverTimeUs = inDiscoverTimeUs;
24065 obj->browser = inBrowser;
24066
24067 *outInstance = obj;
24068 obj = NULL;
24069 err = kNoErr;
24070
24071 exit:
24072 if( obj ) _SBServiceInstanceFree( obj );
24073 return( err );
24074 }
24075
24076 //===========================================================================================================================
24077 // _SBServiceInstanceFree
24078 //===========================================================================================================================
24079
24080 static void _SBServiceInstanceFree( SBServiceInstance *inInstance )
24081 {
24082 ForgetMem( &inInstance->name );
24083 ForgetMem( &inInstance->fqdn );
24084 DNSServiceForget( &inInstance->resolve );
24085 ForgetMem( &inInstance->hostname );
24086 ForgetMem( &inInstance->txtPtr );
24087 DNSServiceForget( &inInstance->getAddrInfo );
24088 ForgetSBIPAddressList( &inInstance->ipaddrList );
24089 free( inInstance );
24090 }
24091
24092 //===========================================================================================================================
24093 // _SBIPAddressCreate
24094 //===========================================================================================================================
24095
24096 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
24097 {
24098 OSStatus err;
24099 SBIPAddress * obj;
24100
24101 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
24102 require_action( obj, exit, err = kNoMemoryErr );
24103
24104 SockAddrCopy( inSockAddr, &obj->sip );
24105 obj->resolveTimeUs = inResolveTimeUs;
24106
24107 *outIPAddress = obj;
24108 err = kNoErr;
24109
24110 exit:
24111 return( err );
24112 }
24113
24114 //===========================================================================================================================
24115 // _SBIPAddressFree
24116 //===========================================================================================================================
24117
24118 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
24119 {
24120 free( inIPAddress );
24121 }
24122
24123 //===========================================================================================================================
24124 // _SBIPAddressFreeList
24125 //===========================================================================================================================
24126
24127 static void _SBIPAddressFreeList( SBIPAddress *inList )
24128 {
24129 SBIPAddress * ipaddr;
24130
24131 while( ( ipaddr = inList ) != NULL )
24132 {
24133 inList = ipaddr->next;
24134 _SBIPAddressFree( ipaddr );
24135 }
24136 }
24137
24138 //===========================================================================================================================
24139 // _SBRDomainCreate
24140 //===========================================================================================================================
24141
24142 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
24143 {
24144 OSStatus err;
24145 SBRDomain * obj;
24146
24147 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
24148 require_action( obj, exit, err = kNoMemoryErr );
24149
24150 obj->name = strdup( inName );
24151 require_action( obj->name, exit, err = kNoMemoryErr );
24152
24153 *outDomain = obj;
24154 obj = NULL;
24155 err = kNoErr;
24156
24157 exit:
24158 if( obj ) _SBRDomainFree( obj );
24159 return( err );
24160 }
24161
24162 //===========================================================================================================================
24163 // _SBRDomainFree
24164 //===========================================================================================================================
24165
24166 static void _SBRDomainFree( SBRDomain *inDomain )
24167 {
24168 SBRServiceType * type;
24169
24170 ForgetMem( &inDomain->name );
24171 while( ( type = inDomain->typeList ) != NULL )
24172 {
24173 inDomain->typeList = type->next;
24174 _SBRServiceTypeFree( type );
24175 }
24176 free( inDomain );
24177 }
24178
24179 //===========================================================================================================================
24180 // _SBRServiceTypeCreate
24181 //===========================================================================================================================
24182
24183 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
24184 {
24185 OSStatus err;
24186 SBRServiceType * obj;
24187
24188 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
24189 require_action( obj, exit, err = kNoMemoryErr );
24190
24191 obj->name = strdup( inName );
24192 require_action( obj->name, exit, err = kNoMemoryErr );
24193
24194 *outType = obj;
24195 obj = NULL;
24196 err = kNoErr;
24197
24198 exit:
24199 if( obj ) _SBRServiceTypeFree( obj );
24200 return( err );
24201 }
24202
24203 //===========================================================================================================================
24204 // _SBRServiceTypeFree
24205 //===========================================================================================================================
24206
24207 static void _SBRServiceTypeFree( SBRServiceType *inType )
24208 {
24209 SBRServiceInstance * instance;
24210
24211 ForgetMem( &inType->name );
24212 while( ( instance = inType->instanceList ) != NULL )
24213 {
24214 inType->instanceList = instance->next;
24215 _SBRServiceInstanceFree( instance );
24216 }
24217 free( inType );
24218 }
24219
24220 //===========================================================================================================================
24221 // _SBRServiceInstanceCreate
24222 //===========================================================================================================================
24223
24224 static OSStatus
24225 _SBRServiceInstanceCreate(
24226 const char * inName,
24227 uint32_t inInterfaceIndex,
24228 const char * inHostname,
24229 uint16_t inPort,
24230 const uint8_t * inTXTPtr,
24231 size_t inTXTLen,
24232 uint64_t inDiscoverTimeUs,
24233 uint64_t inResolveTimeUs,
24234 SBRServiceInstance ** outInstance )
24235 {
24236 OSStatus err;
24237 SBRServiceInstance * obj;
24238
24239 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
24240 require_action( obj, exit, err = kNoMemoryErr );
24241
24242 obj->name = strdup( inName );
24243 require_action( obj->name, exit, err = kNoMemoryErr );
24244
24245 if( inHostname )
24246 {
24247 obj->hostname = strdup( inHostname );
24248 require_action( obj->hostname, exit, err = kNoMemoryErr );
24249 }
24250 if( inTXTLen > 0 )
24251 {
24252 obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
24253 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
24254 obj->txtLen = inTXTLen;
24255 }
24256 obj->discoverTimeUs = inDiscoverTimeUs;
24257 obj->resolveTimeUs = inResolveTimeUs;
24258 obj->ifIndex = inInterfaceIndex;
24259 obj->port = inPort;
24260
24261 *outInstance = obj;
24262 obj = NULL;
24263 err = kNoErr;
24264
24265 exit:
24266 if( obj ) _SBRServiceInstanceFree( obj );
24267 return( err );
24268 }
24269
24270 //===========================================================================================================================
24271 // _SBRServiceInstanceFree
24272 //===========================================================================================================================
24273
24274 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
24275 {
24276 SBRIPAddress * ipaddr;
24277
24278 ForgetMem( &inInstance->name );
24279 ForgetMem( &inInstance->hostname );
24280 ForgetMem( &inInstance->txtPtr );
24281 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
24282 {
24283 inInstance->ipaddrList = ipaddr->next;
24284 _SBRIPAddressFree( ipaddr );
24285 }
24286 free( inInstance );
24287 }
24288
24289 //===========================================================================================================================
24290 // _SBRIPAddressCreate
24291 //===========================================================================================================================
24292
24293 static OSStatus
24294 _SBRIPAddressCreate(
24295 const struct sockaddr * inSockAddr,
24296 uint64_t inResolveTimeUs,
24297 SBRIPAddress ** outIPAddress )
24298 {
24299 OSStatus err;
24300 SBRIPAddress * obj;
24301
24302 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
24303 require_action( obj, exit, err = kNoMemoryErr );
24304
24305 SockAddrCopy( inSockAddr, &obj->sip );
24306 obj->resolveTimeUs = inResolveTimeUs;
24307
24308 *outIPAddress = obj;
24309 err = kNoErr;
24310
24311 exit:
24312 return( err );
24313 }
24314
24315 //===========================================================================================================================
24316 // _SBRIPAddressFree
24317 //===========================================================================================================================
24318
24319 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
24320 {
24321 free( inIPAddress );
24322 }
24323
24324 //===========================================================================================================================
24325 // _SocketWriteAll
24326 //
24327 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
24328 //===========================================================================================================================
24329
24330 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
24331 {
24332 OSStatus err;
24333 const uint8_t * src;
24334 const uint8_t * end;
24335 fd_set writeSet;
24336 struct timeval timeout;
24337 ssize_t n;
24338
24339 FD_ZERO( &writeSet );
24340 src = (const uint8_t *) inData;
24341 end = src + inSize;
24342 while( src < end )
24343 {
24344 FD_SET( inSock, &writeSet );
24345 timeout.tv_sec = inTimeoutSecs;
24346 timeout.tv_usec = 0;
24347 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
24348 if( n == 0 ) { err = kTimeoutErr; goto exit; }
24349 err = map_socket_value_errno( inSock, n > 0, n );
24350 require_noerr( err, exit );
24351
24352 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
24353 err = map_socket_value_errno( inSock, n >= 0, n );
24354 if( err == EINTR ) continue;
24355 require_noerr( err, exit );
24356
24357 src += n;
24358 }
24359 err = kNoErr;
24360
24361 exit:
24362 return( err );
24363 }
24364
24365 //===========================================================================================================================
24366 // _ParseIPv4Address
24367 //
24368 // Warning: "inBuffer" may be modified even in error cases.
24369 //
24370 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
24371 //===========================================================================================================================
24372
24373 static OSStatus _ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
24374 {
24375 OSStatus err;
24376 uint8_t * dst;
24377 int segments;
24378 int sawDigit;
24379 int c;
24380 int v;
24381
24382 check( inBuffer );
24383 check( outStr );
24384
24385 dst = inBuffer;
24386 *dst = 0;
24387 sawDigit = 0;
24388 segments = 0;
24389 for( ; ( c = *inStr ) != '\0'; ++inStr )
24390 {
24391 if( isdigit_safe( c ) )
24392 {
24393 v = ( *dst * 10 ) + ( c - '0' );
24394 require_action_quiet( v <= 255, exit, err = kRangeErr );
24395 *dst = (uint8_t) v;
24396 if( !sawDigit )
24397 {
24398 ++segments;
24399 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
24400 sawDigit = 1;
24401 }
24402 }
24403 else if( ( c == '.' ) && sawDigit )
24404 {
24405 require_action_quiet( segments < 4, exit, err = kMalformedErr );
24406 *++dst = 0;
24407 sawDigit = 0;
24408 }
24409 else
24410 {
24411 break;
24412 }
24413 }
24414 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
24415
24416 *outStr = inStr;
24417 err = kNoErr;
24418
24419 exit:
24420 return( err );
24421 }
24422
24423 //===========================================================================================================================
24424 // _StringToIPv4Address
24425 //
24426 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
24427 //===========================================================================================================================
24428
24429 static OSStatus
24430 _StringToIPv4Address(
24431 const char * inStr,
24432 StringToIPAddressFlags inFlags,
24433 uint32_t * outIP,
24434 int * outPort,
24435 uint32_t * outSubnet,
24436 uint32_t * outRouter,
24437 const char ** outStr )
24438 {
24439 OSStatus err;
24440 uint8_t buf[ 4 ];
24441 int c;
24442 uint32_t ip;
24443 int hasPort;
24444 int port;
24445 int hasPrefix;
24446 int prefix;
24447 uint32_t subnetMask;
24448 uint32_t router;
24449
24450 require_action( inStr, exit, err = kParamErr );
24451
24452 // Parse the address-only part of the address (e.g. "1.2.3.4").
24453
24454 err = _ParseIPv4Address( inStr, buf, &inStr );
24455 require_noerr_quiet( err, exit );
24456 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
24457 c = *inStr;
24458
24459 // Parse the port (if any).
24460
24461 hasPort = 0;
24462 port = 0;
24463 if( c == ':' )
24464 {
24465 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
24466 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
24467 require_action_quiet( port <= 65535, exit, err = kRangeErr );
24468 hasPort = 1;
24469 }
24470
24471 // Parse the prefix length (if any).
24472
24473 hasPrefix = 0;
24474 prefix = 0;
24475 subnetMask = 0;
24476 router = 0;
24477 if( c == '/' )
24478 {
24479 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
24480 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
24481 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
24482 hasPrefix = 1;
24483
24484 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
24485 router = ( ip & subnetMask ) | 1;
24486 }
24487
24488 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
24489
24490 if( outIP ) *outIP = ip;
24491 if( outPort && hasPort ) *outPort = port;
24492 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
24493 if( outRouter && hasPrefix ) *outRouter = router;
24494 if( outStr ) *outStr = inStr;
24495 err = kNoErr;
24496
24497 exit:
24498 return( err );
24499 }
24500
24501 //===========================================================================================================================
24502 // _ParseIPv6Address
24503 //
24504 // Note: Parsed according to the rules specified in RFC 3513.
24505 // Warning: "inBuffer" may be modified even in error cases.
24506 //
24507 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24508 //===========================================================================================================================
24509
24510 static OSStatus _ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
24511 {
24512 // Table to map uppercase hex characters - '0' to their numeric values.
24513 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
24514 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 };
24515 OSStatus err;
24516 const char * ptr;
24517 uint8_t * dst;
24518 uint8_t * lim;
24519 uint8_t * colonPtr;
24520 int c;
24521 int sawDigit;
24522 unsigned int v;
24523 int i;
24524 int n;
24525
24526 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
24527
24528 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
24529
24530 // Special case leading :: (e.g. "::1") to simplify processing later.
24531
24532 if( *inStr == ':' )
24533 {
24534 ++inStr;
24535 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
24536 }
24537
24538 // Parse the address.
24539
24540 ptr = inStr;
24541 dst = inBuffer;
24542 lim = dst + 16;
24543 colonPtr = NULL;
24544 sawDigit = 0;
24545 v = 0;
24546 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
24547 {
24548 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
24549 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
24550 {
24551 c -= '0';
24552 check( c < (int) countof( kASCIItoHexTable ) );
24553 v = ( v << 4 ) | kASCIItoHexTable[ c ];
24554 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
24555 sawDigit = 1;
24556 continue;
24557 }
24558 if( c == ':' )
24559 {
24560 ptr = inStr;
24561 if( !sawDigit )
24562 {
24563 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
24564 colonPtr = dst;
24565 continue;
24566 }
24567 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
24568 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
24569 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
24570 *dst++ = (uint8_t)( v & 0xFF );
24571 sawDigit = 0;
24572 v = 0;
24573 continue;
24574 }
24575
24576 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
24577
24578 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
24579 {
24580 err = _ParseIPv4Address( ptr, dst, &inStr );
24581 require_noerr_quiet( err, exit );
24582 dst += 4;
24583 sawDigit = 0;
24584 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
24585 }
24586 break;
24587 }
24588 if( sawDigit )
24589 {
24590 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
24591 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
24592 *dst++ = (uint8_t)( v & 0xFF );
24593 }
24594 check( dst <= lim );
24595 if( colonPtr )
24596 {
24597 require_action_quiet( dst < lim, exit, err = kOverrunErr );
24598 n = (int)( dst - colonPtr );
24599 for( i = 1; i <= n; ++i )
24600 {
24601 lim[ -i ] = colonPtr[ n - i ];
24602 colonPtr[ n - i ] = 0;
24603 }
24604 dst = lim;
24605 }
24606 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
24607
24608 *outStr = inStr - 1;
24609 err = kNoErr;
24610
24611 exit:
24612 return( err );
24613 }
24614
24615 //===========================================================================================================================
24616 // _ParseIPv6Scope
24617 //
24618 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24619 //===========================================================================================================================
24620
24621 static OSStatus _ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
24622 {
24623 #if( TARGET_OS_POSIX )
24624 OSStatus err;
24625 char scopeStr[ 64 ];
24626 char * dst;
24627 char * lim;
24628 int c;
24629 uint32_t scope;
24630 const char * ptr;
24631
24632 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
24633
24634 dst = scopeStr;
24635 lim = dst + ( countof( scopeStr ) - 1 );
24636 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
24637 {
24638 *dst++ = *inStr++;
24639 }
24640 *dst = '\0';
24641 check( dst <= lim );
24642
24643 // First try to map as a name and if that fails, treat it as a numeric scope.
24644
24645 scope = if_nametoindex( scopeStr );
24646 if( scope == 0 )
24647 {
24648 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
24649 {
24650 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
24651 }
24652 require_action_quiet( c == '\0', exit, err = kMalformedErr );
24653 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
24654 }
24655
24656 *outScope = scope;
24657 *outStr = inStr;
24658 err = kNoErr;
24659
24660 exit:
24661 return( err );
24662 #else
24663 OSStatus err;
24664 uint32_t scope;
24665 const char * start;
24666 int c;
24667
24668 scope = 0;
24669 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
24670 {
24671 scope = ( scope * 10 ) + ( c - '0' );
24672 }
24673 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
24674
24675 *outScope = scope;
24676 *outStr = inStr;
24677 err = kNoErr;
24678
24679 exit:
24680 return( err );
24681 #endif
24682 }
24683
24684 //===========================================================================================================================
24685 // _StringToIPv6Address
24686 //
24687 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
24688 //===========================================================================================================================
24689
24690 static OSStatus
24691 _StringToIPv6Address(
24692 const char * inStr,
24693 StringToIPAddressFlags inFlags,
24694 uint8_t outIPv6[ 16 ],
24695 uint32_t * outScope,
24696 int * outPort,
24697 int * outPrefix,
24698 const char ** outStr )
24699 {
24700 OSStatus err;
24701 uint8_t ipv6[ 16 ];
24702 int c;
24703 int hasScope;
24704 uint32_t scope;
24705 int hasPort;
24706 int port;
24707 int hasPrefix;
24708 int prefix;
24709 int hasBracket;
24710 int i;
24711
24712 require_action( inStr, exit, err = kParamErr );
24713
24714 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
24715
24716 // Parse the address-only part of the address (e.g. "1::1").
24717
24718 err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
24719 require_noerr_quiet( err, exit );
24720 c = *inStr;
24721
24722 // Parse the scope, port, or prefix length.
24723
24724 hasScope = 0;
24725 scope = 0;
24726 hasPort = 0;
24727 port = 0;
24728 hasPrefix = 0;
24729 prefix = 0;
24730 hasBracket = 0;
24731 for( ;; )
24732 {
24733 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
24734 {
24735 require_action_quiet( !hasScope, exit, err = kMalformedErr );
24736 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
24737 ++inStr;
24738 err = _ParseIPv6Scope( inStr, &scope, &inStr );
24739 require_noerr_quiet( err, exit );
24740 hasScope = 1;
24741 c = *inStr;
24742 }
24743 else if( c == ':' ) // Port (e.g. ":80")
24744 {
24745 require_action_quiet( !hasPort, exit, err = kMalformedErr );
24746 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
24747 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
24748 require_action_quiet( port <= 65535, exit, err = kRangeErr );
24749 hasPort = 1;
24750 }
24751 else if( c == '/' ) // Prefix Length (e.g. "/64")
24752 {
24753 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
24754 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
24755 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
24756 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
24757 hasPrefix = 1;
24758 }
24759 else if( c == ']' )
24760 {
24761 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
24762 hasBracket = 1;
24763 c = *( ++inStr );
24764 }
24765 else
24766 {
24767 break;
24768 }
24769 }
24770
24771 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
24772
24773 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
24774 if( outScope && hasScope ) *outScope = scope;
24775 if( outPort && hasPort ) *outPort = port;
24776 if( outPrefix && hasPrefix ) *outPrefix = prefix;
24777 if( outStr ) *outStr = inStr;
24778 err = kNoErr;
24779
24780 exit:
24781 return( err );
24782 }
24783
24784 //===========================================================================================================================
24785 // _StringArray_Free
24786 //
24787 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
24788 //===========================================================================================================================
24789
24790 static void _StringArray_Free( char **inArray, size_t inCount )
24791 {
24792 size_t i;
24793
24794 for( i = 0; i < inCount; ++i )
24795 {
24796 free( inArray[ i ] );
24797 }
24798 if( inCount > 0 ) free( inArray );
24799 }
24800
24801 //===========================================================================================================================
24802 // _ParseQuotedEscapedString
24803 //
24804 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24805 //===========================================================================================================================
24806
24807 static Boolean
24808 _ParseQuotedEscapedString(
24809 const char * inSrc,
24810 const char * inEnd,
24811 const char * inDelimiters,
24812 char * inBuf,
24813 size_t inMaxLen,
24814 size_t * outCopiedLen,
24815 size_t * outTotalLen,
24816 const char ** outSrc )
24817 {
24818 const unsigned char * src;
24819 const unsigned char * end;
24820 unsigned char * dst;
24821 unsigned char * lim;
24822 unsigned char c;
24823 unsigned char c2;
24824 size_t totalLen;
24825 Boolean singleQuote;
24826 Boolean doubleQuote;
24827
24828 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
24829 src = (const unsigned char *) inSrc;
24830 end = (const unsigned char *) inEnd;
24831 dst = (unsigned char *) inBuf;
24832 lim = dst + inMaxLen;
24833 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
24834 if( src >= end ) return( false );
24835
24836 // Parse each argument from the string.
24837 //
24838 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
24839
24840 totalLen = 0;
24841 singleQuote = false;
24842 doubleQuote = false;
24843 while( src < end )
24844 {
24845 c = *src++;
24846 if( singleQuote )
24847 {
24848 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
24849
24850 if( c == '\'' )
24851 {
24852 singleQuote = false;
24853 continue;
24854 }
24855 }
24856 else if( doubleQuote )
24857 {
24858 // Double quotes protect everything except double quotes and backslashes. A backslash can be
24859 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
24860 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
24861 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
24862 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
24863
24864 if( c == '"' )
24865 {
24866 doubleQuote = false;
24867 continue;
24868 }
24869 else if( c == '\\' )
24870 {
24871 if( src < end )
24872 {
24873 c2 = *src;
24874 if( ( c2 == '"' ) || ( c2 == '\\' ) )
24875 {
24876 ++src;
24877 c = c2;
24878 }
24879 else if( c2 == '\n' )
24880 {
24881 ++src;
24882 continue;
24883 }
24884 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
24885 {
24886 ++src;
24887 c = c2;
24888 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
24889 {
24890 c = HexPairToByte( src );
24891 src += 2;
24892 }
24893 }
24894 else if( isoctal_safe( c2 ) )
24895 {
24896 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
24897 {
24898 c = OctalTripleToByte( src );
24899 src += 3;
24900 }
24901 }
24902 }
24903 }
24904 }
24905 else if( strchr( inDelimiters, c ) )
24906 {
24907 break;
24908 }
24909 else if( c == '\\' )
24910 {
24911 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
24912 // A backslash followed by a newline disappears completely.
24913 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
24914 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
24915
24916 if( src < end )
24917 {
24918 c = *src;
24919 if( c == '\n' )
24920 {
24921 ++src;
24922 continue;
24923 }
24924 else if( ( c == 'x' ) || ( c == 'X' ) )
24925 {
24926 ++src;
24927 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
24928 {
24929 c = HexPairToByte( src );
24930 src += 2;
24931 }
24932 }
24933 else if( isoctal_safe( c ) )
24934 {
24935 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
24936 {
24937 c = OctalTripleToByte( src );
24938 src += 3;
24939 }
24940 else
24941 {
24942 ++src;
24943 }
24944 }
24945 else
24946 {
24947 ++src;
24948 }
24949 }
24950 }
24951 else if( c == '\'' )
24952 {
24953 singleQuote = true;
24954 continue;
24955 }
24956 else if( c == '"' )
24957 {
24958 doubleQuote = true;
24959 continue;
24960 }
24961
24962 if( dst < lim )
24963 {
24964 if( inBuf ) *dst = c;
24965 ++dst;
24966 }
24967 ++totalLen;
24968 }
24969
24970 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
24971 if( outTotalLen ) *outTotalLen = totalLen;
24972 if( outSrc ) *outSrc = (const char *) src;
24973 return( true );
24974 }
24975
24976 //===========================================================================================================================
24977 // _ServerSocketOpenEx2
24978 //
24979 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
24980 //===========================================================================================================================
24981
24982 static OSStatus
24983 _ServerSocketOpenEx2(
24984 int inFamily,
24985 int inType,
24986 int inProtocol,
24987 const void * inAddr,
24988 int inPort,
24989 int * outPort,
24990 int inRcvBufSize,
24991 Boolean inNoPortReuse,
24992 SocketRef * outSock )
24993 {
24994 OSStatus err;
24995 int port;
24996 SocketRef sock;
24997 int name;
24998 int option;
24999 sockaddr_ip sip;
25000 socklen_t len;
25001
25002 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
25003
25004 sock = socket( inFamily, inType, inProtocol );
25005 err = map_socket_creation_errno( sock );
25006 require_noerr_quiet( err, exit );
25007
25008 #if( defined( SO_NOSIGPIPE ) )
25009 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
25010 #endif
25011
25012 err = SocketMakeNonBlocking( sock );
25013 require_noerr( err, exit );
25014
25015 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
25016 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
25017 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
25018 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
25019
25020 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
25021 check_noerr( err );
25022
25023 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
25024
25025 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
25026 {
25027 option = 1;
25028 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
25029 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
25030 err = map_socket_noerr_errno( sock, err );
25031 require_noerr( err, exit );
25032 }
25033
25034 if( inFamily == AF_INET )
25035 {
25036 // Bind to the port. If it fails, retry with a dynamic port.
25037
25038 memset( &sip.v4, 0, sizeof( sip.v4 ) );
25039 SIN_LEN_SET( &sip.v4 );
25040 sip.v4.sin_family = AF_INET;
25041 sip.v4.sin_port = htons( (uint16_t) port );
25042 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
25043 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
25044 err = map_socket_noerr_errno( sock, err );
25045 if( err && ( inPort < 0 ) )
25046 {
25047 sip.v4.sin_port = 0;
25048 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
25049 err = map_socket_noerr_errno( sock, err );
25050 }
25051 require_noerr( err, exit );
25052 }
25053 #if( defined( AF_INET6 ) )
25054 else if( inFamily == AF_INET6 )
25055 {
25056 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
25057
25058 option = 1;
25059 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
25060 err = map_socket_noerr_errno( sock, err );
25061 require_noerr( err, exit );
25062
25063 // Bind to the port. If it fails, retry with a dynamic port.
25064
25065 memset( &sip.v6, 0, sizeof( sip.v6 ) );
25066 SIN6_LEN_SET( &sip.v6 );
25067 sip.v6.sin6_family = AF_INET6;
25068 sip.v6.sin6_port = htons( (uint16_t) port );
25069 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
25070 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
25071 err = map_socket_noerr_errno( sock, err );
25072 if( err && ( inPort < 0 ) )
25073 {
25074 sip.v6.sin6_port = 0;
25075 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
25076 err = map_socket_noerr_errno( sock, err );
25077 }
25078 require_noerr( err, exit );
25079 }
25080 #endif
25081 else
25082 {
25083 dlogassert( "Unsupported family: %d", inFamily );
25084 err = kUnsupportedErr;
25085 goto exit;
25086 }
25087
25088 if( inType == SOCK_STREAM )
25089 {
25090 err = listen( sock, SOMAXCONN );
25091 err = map_socket_noerr_errno( sock, err );
25092 if( err )
25093 {
25094 err = listen( sock, 5 );
25095 err = map_socket_noerr_errno( sock, err );
25096 require_noerr( err, exit );
25097 }
25098 }
25099
25100 if( outPort )
25101 {
25102 len = (socklen_t) sizeof( sip );
25103 err = getsockname( sock, &sip.sa, &len );
25104 err = map_socket_noerr_errno( sock, err );
25105 require_noerr( err, exit );
25106
25107 *outPort = SockAddrGetPort( &sip );
25108 }
25109 *outSock = sock;
25110 sock = kInvalidSocketRef;
25111
25112 exit:
25113 ForgetSocket( &sock );
25114 return( err );
25115 }
25116
25117 //===========================================================================================================================
25118 // _memdup
25119 //
25120 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25121 //===========================================================================================================================
25122
25123 static void * _memdup( const void *inPtr, size_t inLen )
25124 {
25125 void * mem;
25126
25127 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
25128 require( mem, exit );
25129 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
25130
25131 exit:
25132 return( mem );
25133 }
25134
25135 //===========================================================================================================================
25136 // _memicmp
25137 //
25138 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25139 //===========================================================================================================================
25140
25141 static int _memicmp( const void *inP1, const void *inP2, size_t inLen )
25142 {
25143 const unsigned char * p1;
25144 const unsigned char * e1;
25145 const unsigned char * p2;
25146 int c1;
25147 int c2;
25148
25149 p1 = (const unsigned char *) inP1;
25150 e1 = p1 + inLen;
25151 p2 = (const unsigned char *) inP2;
25152 while( p1 < e1 )
25153 {
25154 c1 = *p1++;
25155 c2 = *p2++;
25156 c1 = tolower( c1 );
25157 c2 = tolower( c2 );
25158 if( c1 < c2 ) return( -1 );
25159 if( c1 > c2 ) return( 1 );
25160 }
25161 return( 0 );
25162 }
25163
25164 //===========================================================================================================================
25165 // _FNV1
25166 //
25167 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
25168 //===========================================================================================================================
25169
25170 static uint32_t _FNV1( const void *inData, size_t inSize )
25171 {
25172 const uint8_t * src = (const uint8_t *) inData;
25173 const uint8_t * const end = src + inSize;
25174 uint32_t hash;
25175
25176 hash = 0x811c9dc5U;
25177 while( src != end )
25178 {
25179 hash *= 0x01000193;
25180 hash ^= *src++;
25181 }
25182 return( hash );
25183 }