]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dnssdutil.c
mDNSResponder-1096.0.2.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 #endif
48
49 //===========================================================================================================================
50 // Versioning
51 //===========================================================================================================================
52
53 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
54
55 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
56 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
57 #endif
58
59 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
60
61 //===========================================================================================================================
62 // DNS-SD
63 //===========================================================================================================================
64
65 // DNS-SD API flag descriptors
66
67 #define kDNSServiceFlagsDescriptors \
68 "\x00" "AutoTrigger\0" \
69 "\x01" "Add\0" \
70 "\x02" "Default\0" \
71 "\x03" "NoAutoRename\0" \
72 "\x04" "Shared\0" \
73 "\x05" "Unique\0" \
74 "\x06" "BrowseDomains\0" \
75 "\x07" "RegistrationDomains\0" \
76 "\x08" "LongLivedQuery\0" \
77 "\x09" "AllowRemoteQuery\0" \
78 "\x0A" "ForceMulticast\0" \
79 "\x0B" "KnownUnique\0" \
80 "\x0C" "ReturnIntermediates\0" \
81 "\x0D" "DenyConstrained\0" \
82 "\x0E" "ShareConnection\0" \
83 "\x0F" "SuppressUnusable\0" \
84 "\x10" "Timeout\0" \
85 "\x11" "IncludeP2P\0" \
86 "\x12" "WakeOnResolve\0" \
87 "\x13" "BackgroundTrafficClass\0" \
88 "\x14" "IncludeAWDL\0" \
89 "\x15" "Validate\0" \
90 "\x16" "UnicastResponse\0" \
91 "\x17" "ValidateOptional\0" \
92 "\x18" "WakeOnlyService\0" \
93 "\x19" "ThresholdOne\0" \
94 "\x1A" "ThresholdFinder\0" \
95 "\x1B" "DenyCellular\0" \
96 "\x1C" "ServiceIndex\0" \
97 "\x1D" "DenyExpensive\0" \
98 "\x1E" "PathEvaluationDone\0" \
99 "\x1F" "AllowExpiredAnswers\0" \
100 "\x00"
101
102 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
103
104 #define kDNSServiceProtocolDescriptors \
105 "\x00" "IPv4\0" \
106 "\x01" "IPv6\0" \
107 "\x04" "UDP\0" \
108 "\x05" "TCP\0" \
109 "\x00"
110
111 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
112
113 //===========================================================================================================================
114 // DNS
115 //===========================================================================================================================
116
117 #define kDNSPort 53
118 #define kDNSMaxUDPMessageSize 512
119 #define kDNSMaxTCPMessageSize UINT16_MAX
120
121 #define kDNSRecordDataLengthMax UINT16_MAX
122
123 //===========================================================================================================================
124 // mDNS
125 //===========================================================================================================================
126
127 #define kMDNSPort 5353
128
129 #define kDefaultMDNSMessageID 0
130 #define kDefaultMDNSQueryFlags 0
131
132 #define kQClassUnicastResponseBit ( 1U << 15 )
133 #define kRRClassCacheFlushBit ( 1U << 15 )
134
135 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
136
137 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
138 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
139
140 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
141
142 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
143
144 #define kLocalStr "\x05" "local"
145 #define kLocalLabel ( (const uint8_t *) kLocalStr )
146 #define kLocalName ( (const uint8_t *) kLocalStr )
147 #define kLocalNameLen sizeof( kLocalStr )
148
149 //===========================================================================================================================
150 // Test Address Blocks
151 //===========================================================================================================================
152
153 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
154
155 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
156
157 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
158
159 static const uint8_t kDNSServerBaseAddrV6[] =
160 {
161 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
162 };
163
164 static const uint8_t kMDNSReplierBaseAddrV6[] =
165 {
166 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
167 };
168
169 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
170 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
171
172 // Bad IPv4 and IPv6 Address Blocks
173 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
174 // in 203.0.113.0/24 nor 2001:db8:1::/120.
175
176 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
177
178 static const uint8_t kDNSServerBadBaseAddrV6[] =
179 {
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
181 };
182
183 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
184
185 //===========================================================================================================================
186 // Misc.
187 //===========================================================================================================================
188
189 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
190 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
191
192 #if( !defined( kWhiteSpaceCharSet ) )
193 #define kWhiteSpaceCharSet "\t\n\v\f\r "
194 #endif
195
196 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
197
198 #if( !defined( strcpy_literal ) )
199 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
200 #endif
201
202 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
203 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
204
205 #define kNoSuchRecordStr "No Such Record"
206 #define kNoSuchRecordAStr "No Such Record (A)"
207 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
208
209 #define kRootLabel ( (const uint8_t *) "" )
210
211 //===========================================================================================================================
212 // Gerneral Command Options
213 //===========================================================================================================================
214
215 // Command option macros
216
217 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
218 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
219 (SHORT_HELP), NULL )
220
221 #define kRequiredOptionSuffix " [REQUIRED]"
222
223 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
224 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
225 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
226 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
227
228 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
229 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
230
231 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
232 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
233 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
234 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
235
236 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
237 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
238 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
239 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
240
241 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
242 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
243
244 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
245 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
246 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
247 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
248
249 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
250 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
251
252 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
253 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
254 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
255 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
256
257 // DNS-SD API flag options
258
259 static int gDNSSDFlags = 0;
260 static int gDNSSDFlag_AllowExpiredAnswers = false;
261 static int gDNSSDFlag_BrowseDomains = false;
262 static int gDNSSDFlag_DenyCellular = false;
263 static int gDNSSDFlag_DenyConstrained = false;
264 static int gDNSSDFlag_DenyExpensive = false;
265 static int gDNSSDFlag_ForceMulticast = false;
266 static int gDNSSDFlag_IncludeAWDL = false;
267 static int gDNSSDFlag_KnownUnique = false;
268 static int gDNSSDFlag_NoAutoRename = false;
269 static int gDNSSDFlag_PathEvaluationDone = false;
270 static int gDNSSDFlag_RegistrationDomains = false;
271 static int gDNSSDFlag_ReturnIntermediates = false;
272 static int gDNSSDFlag_Shared = false;
273 static int gDNSSDFlag_SuppressUnusable = false;
274 static int gDNSSDFlag_Timeout = false;
275 static int gDNSSDFlag_UnicastResponse = false;
276 static int gDNSSDFlag_Unique = false;
277 static int gDNSSDFlag_WakeOnResolve = false;
278
279 #define DNSSDFlagsOption() \
280 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
281 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
282
283 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
284 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
285
286 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
287 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
288 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
289 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
290 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
291 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
292 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
293 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
294 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
295 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
296 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
297 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
298 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
299 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
300 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
301 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
302
303 // Interface option
304
305 static const char * gInterface = NULL;
306
307 #define InterfaceOption() \
308 StringOption( 'i', "interface", &gInterface, "interface", \
309 "Network interface by name or index. Use index -1 for local-only.", false )
310
311 // Connection options
312
313 #define kConnectionArg_Normal ""
314 #define kConnectionArgPrefix_PID "pid:"
315 #define kConnectionArgPrefix_UUID "uuid:"
316
317 static const char * gConnectionOpt = kConnectionArg_Normal;
318
319 #define ConnectionOptions() \
320 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
321 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
322 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
323
324 #define kConnectionSection_Name "Connection Option"
325 #define kConnectionSection_Text \
326 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
327 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
328 "specifying the connection option without an argument, i.e.,\n" \
329 "\n" \
330 " --connection\n" \
331 "\n" \
332 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
333 "\n" \
334 " --connection=pid:<PID>\n" \
335 "\n" \
336 "to specify the delegator by PID, or use\n" \
337 "\n" \
338 " --connection=uuid:<UUID>\n" \
339 "\n" \
340 "to specify the delegator by UUID.\n" \
341 "\n" \
342 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
343 "\n" \
344 " --no-connection\n"
345
346 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
347
348 // Help text for record data options
349
350 #define kRDataArgPrefix_Domain "domain:"
351 #define kRDataArgPrefix_File "file:"
352 #define kRDataArgPrefix_HexString "hex:"
353 #define kRDataArgPrefix_IPv4 "ipv4:"
354 #define kRDataArgPrefix_IPv6 "ipv6:"
355 #define kRDataArgPrefix_SRV "srv:"
356 #define kRDataArgPrefix_String "string:"
357 #define kRDataArgPrefix_TXT "txt:"
358
359 #define kRecordDataSection_Name "Record Data Arguments"
360 #define kRecordDataSection_Text \
361 "A record data argument is specified in one of the following formats:\n" \
362 "\n" \
363 "Format Syntax Example\n" \
364 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
365 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
366 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
367 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
368 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
369 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
370 "String string:<string> string:'\\x09color=red'\n" \
371 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
372 "\n" \
373 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
374
375 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
376
377 //===========================================================================================================================
378 // Output Formatting
379 //===========================================================================================================================
380
381 #define kOutputFormatStr_JSON "json"
382 #define kOutputFormatStr_XML "xml"
383 #define kOutputFormatStr_Binary "binary"
384
385 typedef enum
386 {
387 kOutputFormatType_Invalid = 0,
388 kOutputFormatType_JSON = 1,
389 kOutputFormatType_XML = 2,
390 kOutputFormatType_Binary = 3
391
392 } OutputFormatType;
393
394 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
395 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
396 "\n" \
397 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
398 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
399 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
400 "\n" \
401 )
402
403 //===========================================================================================================================
404 // Browse Command Options
405 //===========================================================================================================================
406
407 static char ** gBrowse_ServiceTypes = NULL;
408 static size_t gBrowse_ServiceTypesCount = 0;
409 static const char * gBrowse_Domain = NULL;
410 static int gBrowse_DoResolve = false;
411 static int gBrowse_QueryTXT = false;
412 static int gBrowse_TimeLimitSecs = 0;
413
414 static CLIOption kBrowseOpts[] =
415 {
416 InterfaceOption(),
417 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
418 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
419
420 CLI_OPTION_GROUP( "Flags" ),
421 DNSSDFlagsOption(),
422 DNSSDFlagsOption_IncludeAWDL(),
423
424 CLI_OPTION_GROUP( "Operation" ),
425 ConnectionOptions(),
426 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
427 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
428 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
429
430 ConnectionSection(),
431 CLI_OPTION_END()
432 };
433
434 //===========================================================================================================================
435 // GetAddrInfo Command Options
436 //===========================================================================================================================
437
438 static const char * gGetAddrInfo_Name = NULL;
439 static int gGetAddrInfo_ProtocolIPv4 = false;
440 static int gGetAddrInfo_ProtocolIPv6 = false;
441 static int gGetAddrInfo_OneShot = false;
442 static int gGetAddrInfo_TimeLimitSecs = 0;
443
444 static CLIOption kGetAddrInfoOpts[] =
445 {
446 InterfaceOption(),
447 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
448 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
449 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
450
451 CLI_OPTION_GROUP( "Flags" ),
452 DNSSDFlagsOption(),
453 DNSSDFlagsOption_AllowExpiredAnswers(),
454 DNSSDFlagsOption_DenyCellular(),
455 DNSSDFlagsOption_DenyConstrained(),
456 DNSSDFlagsOption_DenyExpensive(),
457 DNSSDFlagsOption_PathEvalDone(),
458 DNSSDFlagsOption_ReturnIntermediates(),
459 DNSSDFlagsOption_SuppressUnusable(),
460 DNSSDFlagsOption_Timeout(),
461
462 CLI_OPTION_GROUP( "Operation" ),
463 ConnectionOptions(),
464 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
465 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
466
467 ConnectionSection(),
468 CLI_OPTION_END()
469 };
470
471 //===========================================================================================================================
472 // QueryRecord Command Options
473 //===========================================================================================================================
474
475 static const char * gQueryRecord_Name = NULL;
476 static const char * gQueryRecord_Type = NULL;
477 static int gQueryRecord_OneShot = false;
478 static int gQueryRecord_TimeLimitSecs = 0;
479 static int gQueryRecord_RawRData = false;
480
481 static CLIOption kQueryRecordOpts[] =
482 {
483 InterfaceOption(),
484 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
485 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
486
487 CLI_OPTION_GROUP( "Flags" ),
488 DNSSDFlagsOption(),
489 DNSSDFlagsOption_AllowExpiredAnswers(),
490 DNSSDFlagsOption_DenyCellular(),
491 DNSSDFlagsOption_DenyConstrained(),
492 DNSSDFlagsOption_DenyExpensive(),
493 DNSSDFlagsOption_ForceMulticast(),
494 DNSSDFlagsOption_IncludeAWDL(),
495 DNSSDFlagsOption_PathEvalDone(),
496 DNSSDFlagsOption_ReturnIntermediates(),
497 DNSSDFlagsOption_SuppressUnusable(),
498 DNSSDFlagsOption_Timeout(),
499 DNSSDFlagsOption_UnicastResponse(),
500
501 CLI_OPTION_GROUP( "Operation" ),
502 ConnectionOptions(),
503 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
504 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
505 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
506
507 ConnectionSection(),
508 CLI_OPTION_END()
509 };
510
511 //===========================================================================================================================
512 // Register Command Options
513 //===========================================================================================================================
514
515 static const char * gRegister_Name = NULL;
516 static const char * gRegister_Type = NULL;
517 static const char * gRegister_Domain = NULL;
518 static int gRegister_Port = 0;
519 static const char * gRegister_TXT = NULL;
520 static int gRegister_LifetimeMs = -1;
521 static const char ** gAddRecord_Types = NULL;
522 static size_t gAddRecord_TypesCount = 0;
523 static const char ** gAddRecord_Data = NULL;
524 static size_t gAddRecord_DataCount = 0;
525 static const char ** gAddRecord_TTLs = NULL;
526 static size_t gAddRecord_TTLsCount = 0;
527 static const char * gUpdateRecord_Data = NULL;
528 static int gUpdateRecord_DelayMs = 0;
529 static int gUpdateRecord_TTL = 0;
530
531 static CLIOption kRegisterOpts[] =
532 {
533 InterfaceOption(),
534 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
535 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
536 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
537 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
538 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
539
540 CLI_OPTION_GROUP( "Flags" ),
541 DNSSDFlagsOption(),
542 DNSSDFlagsOption_IncludeAWDL(),
543 DNSSDFlagsOption_KnownUnique(),
544 DNSSDFlagsOption_NoAutoRename(),
545
546 CLI_OPTION_GROUP( "Operation" ),
547 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
548
549 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
550 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
551 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
552 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
553
554 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
555 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
556 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
557 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
558
559 RecordDataSection(),
560 CLI_OPTION_END()
561 };
562
563 //===========================================================================================================================
564 // RegisterRecord Command Options
565 //===========================================================================================================================
566
567 static const char * gRegisterRecord_Name = NULL;
568 static const char * gRegisterRecord_Type = NULL;
569 static const char * gRegisterRecord_Data = NULL;
570 static int gRegisterRecord_TTL = 0;
571 static int gRegisterRecord_LifetimeMs = -1;
572 static const char * gRegisterRecord_UpdateData = NULL;
573 static int gRegisterRecord_UpdateDelayMs = 0;
574 static int gRegisterRecord_UpdateTTL = 0;
575
576 static CLIOption kRegisterRecordOpts[] =
577 {
578 InterfaceOption(),
579 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
580 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
581 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
582 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
583
584 CLI_OPTION_GROUP( "Flags" ),
585 DNSSDFlagsOption(),
586 DNSSDFlagsOption_IncludeAWDL(),
587 DNSSDFlagsOption_KnownUnique(),
588 DNSSDFlagsOption_Shared(),
589 DNSSDFlagsOption_Unique(),
590
591 CLI_OPTION_GROUP( "Operation" ),
592 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
593
594 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
595 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
596 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
597 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
598
599 RecordDataSection(),
600 CLI_OPTION_END()
601 };
602
603 //===========================================================================================================================
604 // Resolve Command Options
605 //===========================================================================================================================
606
607 static char * gResolve_Name = NULL;
608 static char * gResolve_Type = NULL;
609 static char * gResolve_Domain = NULL;
610 static int gResolve_TimeLimitSecs = 0;
611
612 static CLIOption kResolveOpts[] =
613 {
614 InterfaceOption(),
615 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
616 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
617 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
618
619 CLI_OPTION_GROUP( "Flags" ),
620 DNSSDFlagsOption(),
621 DNSSDFlagsOption_ForceMulticast(),
622 DNSSDFlagsOption_IncludeAWDL(),
623 DNSSDFlagsOption_ReturnIntermediates(),
624 DNSSDFlagsOption_WakeOnResolve(),
625
626 CLI_OPTION_GROUP( "Operation" ),
627 ConnectionOptions(),
628 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
629
630 ConnectionSection(),
631 CLI_OPTION_END()
632 };
633
634 //===========================================================================================================================
635 // Reconfirm Command Options
636 //===========================================================================================================================
637
638 static const char * gReconfirmRecord_Name = NULL;
639 static const char * gReconfirmRecord_Type = NULL;
640 static const char * gReconfirmRecord_Class = NULL;
641 static const char * gReconfirmRecord_Data = NULL;
642
643 static CLIOption kReconfirmOpts[] =
644 {
645 InterfaceOption(),
646 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
647 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
648 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
649 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
650
651 CLI_OPTION_GROUP( "Flags" ),
652 DNSSDFlagsOption(),
653
654 RecordDataSection(),
655 CLI_OPTION_END()
656 };
657
658 //===========================================================================================================================
659 // getaddrinfo-POSIX Command Options
660 //===========================================================================================================================
661
662 static const char * gGAIPOSIX_HostName = NULL;
663 static const char * gGAIPOSIX_ServName = NULL;
664 static const char * gGAIPOSIX_Family = NULL;
665 static int gGAIPOSIXFlag_AddrConfig = false;
666 static int gGAIPOSIXFlag_All = false;
667 static int gGAIPOSIXFlag_CanonName = false;
668 static int gGAIPOSIXFlag_NumericHost = false;
669 static int gGAIPOSIXFlag_NumericServ = false;
670 static int gGAIPOSIXFlag_Passive = false;
671 static int gGAIPOSIXFlag_V4Mapped = false;
672 #if( defined( AI_V4MAPPED_CFG ) )
673 static int gGAIPOSIXFlag_V4MappedCFG = false;
674 #endif
675 #if( defined( AI_DEFAULT ) )
676 static int gGAIPOSIXFlag_Default = false;
677 #endif
678 #if( defined( AI_UNUSABLE ) )
679 static int gGAIPOSIXFlag_Unusable = false;
680 #endif
681
682 static CLIOption kGetAddrInfoPOSIXOpts[] =
683 {
684 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
685 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
686
687 CLI_OPTION_GROUP( "Hints" ),
688 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
689 "\n"
690 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
691 "address family is specified, then AF_UNSPEC is used.\n"
692 "\n" ),
693 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
694 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
695 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
696 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
697 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
698 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
699 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
700 #if( defined( AI_V4MAPPED_CFG ) )
701 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
702 #endif
703 #if( defined( AI_DEFAULT ) )
704 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
705 #endif
706 #if( defined( AI_UNUSABLE ) )
707 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
708 #endif
709
710 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
711 CLI_OPTION_END()
712 };
713
714 //===========================================================================================================================
715 // ReverseLookup Command Options
716 //===========================================================================================================================
717
718 static const char * gReverseLookup_IPAddr = NULL;
719 static int gReverseLookup_OneShot = false;
720 static int gReverseLookup_TimeLimitSecs = 0;
721
722 static CLIOption kReverseLookupOpts[] =
723 {
724 InterfaceOption(),
725 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
726
727 CLI_OPTION_GROUP( "Flags" ),
728 DNSSDFlagsOption(),
729 DNSSDFlagsOption_ForceMulticast(),
730 DNSSDFlagsOption_ReturnIntermediates(),
731 DNSSDFlagsOption_SuppressUnusable(),
732
733 CLI_OPTION_GROUP( "Operation" ),
734 ConnectionOptions(),
735 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
736 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
737
738 ConnectionSection(),
739 CLI_OPTION_END()
740 };
741
742 //===========================================================================================================================
743 // PortMapping Command Options
744 //===========================================================================================================================
745
746 static int gPortMapping_ProtocolTCP = false;
747 static int gPortMapping_ProtocolUDP = false;
748 static int gPortMapping_InternalPort = 0;
749 static int gPortMapping_ExternalPort = 0;
750 static int gPortMapping_TTL = 0;
751
752 static CLIOption kPortMappingOpts[] =
753 {
754 InterfaceOption(),
755 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
756 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
757 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
758 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
759 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
760
761 CLI_OPTION_GROUP( "Flags" ),
762 DNSSDFlagsOption(),
763
764 CLI_OPTION_GROUP( "Operation" ),
765 ConnectionOptions(),
766
767 ConnectionSection(),
768 CLI_OPTION_END()
769 };
770
771 //===========================================================================================================================
772 // BrowseAll Command Options
773 //===========================================================================================================================
774
775 static const char * gBrowseAll_Domain = NULL;
776 static const char ** gBrowseAll_ServiceTypes = NULL;
777 static size_t gBrowseAll_ServiceTypesCount = 0;
778 static int gBrowseAll_BrowseTimeSecs = 5;
779 static int gBrowseAll_ConnectTimeout = 0;
780
781 static CLIOption kBrowseAllOpts[] =
782 {
783 InterfaceOption(),
784 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
785 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 ),
786
787 CLI_OPTION_GROUP( "Flags" ),
788 DNSSDFlagsOption_IncludeAWDL(),
789
790 CLI_OPTION_GROUP( "Operation" ),
791 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
792 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
793 CLI_OPTION_END()
794 };
795
796 //===========================================================================================================================
797 // GetNameInfo Command Options
798 //===========================================================================================================================
799
800 static void GetNameInfoCmd( void );
801
802 static char * gGetNameInfo_IPAddress = NULL;
803 static int gGetNameInfoFlag_DGram = false;
804 static int gGetNameInfoFlag_NameReqd = false;
805 static int gGetNameInfoFlag_NoFQDN = false;
806 static int gGetNameInfoFlag_NumericHost = false;
807 static int gGetNameInfoFlag_NumericScope = false;
808 static int gGetNameInfoFlag_NumericServ = false;
809
810 static CLIOption kGetNameInfoOpts[] =
811 {
812 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
813
814 CLI_OPTION_GROUP( "Flags" ),
815 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
816 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
817 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
818 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
819 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
820 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
821
822 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
823 CLI_OPTION_END()
824 };
825
826 //===========================================================================================================================
827 // GetAddrInfoStress Command Options
828 //===========================================================================================================================
829
830 static int gGAIStress_TestDurationSecs = 0;
831 static int gGAIStress_ConnectionCount = 0;
832 static int gGAIStress_DurationMinMs = 0;
833 static int gGAIStress_DurationMaxMs = 0;
834 static int gGAIStress_RequestCountMax = 0;
835
836 static CLIOption kGetAddrInfoStressOpts[] =
837 {
838 InterfaceOption(),
839
840 CLI_OPTION_GROUP( "Flags" ),
841 DNSSDFlagsOption_ReturnIntermediates(),
842 DNSSDFlagsOption_SuppressUnusable(),
843
844 CLI_OPTION_GROUP( "Operation" ),
845 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
846 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
847 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
848 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
849 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
850 CLI_OPTION_END()
851 };
852
853 //===========================================================================================================================
854 // DNSQuery Command Options
855 //===========================================================================================================================
856
857 static char * gDNSQuery_Name = NULL;
858 static char * gDNSQuery_Type = "A";
859 static char * gDNSQuery_Server = NULL;
860 static int gDNSQuery_TimeLimitSecs = 5;
861 static int gDNSQuery_UseTCP = false;
862 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
863 static int gDNSQuery_RawRData = false;
864 static int gDNSQuery_Verbose = false;
865
866 #if( TARGET_OS_DARWIN )
867 #define kDNSQueryServerOptionIsRequired false
868 #else
869 #define kDNSQueryServerOptionIsRequired true
870 #endif
871
872 static CLIOption kDNSQueryOpts[] =
873 {
874 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
875 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
876 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
877 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
878 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
879 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
880 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
881 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
882 CLI_OPTION_END()
883 };
884
885 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
886 //===========================================================================================================================
887 // DNSCrypt Command Options
888 //===========================================================================================================================
889
890 static char * gDNSCrypt_ProviderName = NULL;
891 static char * gDNSCrypt_ProviderKey = NULL;
892 static char * gDNSCrypt_Name = NULL;
893 static char * gDNSCrypt_Type = NULL;
894 static char * gDNSCrypt_Server = NULL;
895 static int gDNSCrypt_TimeLimitSecs = 5;
896 static int gDNSCrypt_RawRData = false;
897 static int gDNSCrypt_Verbose = false;
898
899 static CLIOption kDNSCryptOpts[] =
900 {
901 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
902 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
903 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
904 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
905 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
906 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
907 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
908 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
909 CLI_OPTION_END()
910 };
911 #endif
912
913 //===========================================================================================================================
914 // MDNSQuery Command Options
915 //===========================================================================================================================
916
917 static char * gMDNSQuery_Name = NULL;
918 static char * gMDNSQuery_Type = NULL;
919 static int gMDNSQuery_SourcePort = 0;
920 static int gMDNSQuery_IsQU = false;
921 static int gMDNSQuery_RawRData = false;
922 static int gMDNSQuery_UseIPv4 = false;
923 static int gMDNSQuery_UseIPv6 = false;
924 static int gMDNSQuery_AllResponses = false;
925 static int gMDNSQuery_ReceiveSecs = 1;
926
927 static CLIOption kMDNSQueryOpts[] =
928 {
929 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
930 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
931 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
932 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
933 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
934 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
935 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
936 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
937 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
938 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 ),
939 CLI_OPTION_END()
940 };
941
942 //===========================================================================================================================
943 // MDNSCollider Command Options
944 //===========================================================================================================================
945
946 #define kMDNSColliderProgramSection_Intro \
947 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
948 "ought to react to probe queries that match its record's name, if at all.\n" \
949 "\n" \
950 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
951 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
952 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
953 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
954 "accomplished with the following program:\n" \
955 "\n" \
956 " probes 3r; send; wait 5000\n" \
957 "\n" \
958 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
959 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
960 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
961 "is more than enough time for the collider to respond to probe queries.\n" \
962 "\n" \
963 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
964 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
965 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
966
967 #define kMDNSColliderProgramSection_Probes \
968 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
969 "\n" \
970 "Usage: probes [<action-string>]\n" \
971 "\n" \
972 "The syntax for an action-string is\n" \
973 "\n" \
974 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
975 " <action> ::= [<repeat-count>] <action-code>\n" \
976 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
977 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
978 "\n" \
979 "An expanded action-string is defined as\n" \
980 "\n" \
981 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
982 "\n" \
983 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
984 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
985 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
986 "are not allowed.\n" \
987 "\n" \
988 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
989 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
990 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
991 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
992 "\n" \
993 " Code Action\n" \
994 " ---- ------\n" \
995 " n Do nothing.\n" \
996 " r Respond to the probe query.\n" \
997 " u Respond to the probe query via unicast.\n" \
998 " m Respond to the probe query via multicast.\n" \
999 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1000 "\n" \
1001 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1002 "a response nor does it multicast its own probe query.\n"
1003
1004 #define kMDNSColliderProgramSection_Send \
1005 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1006 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1007 "\n" \
1008 "Usage: send\n"
1009
1010 #define kMDNSColliderProgramSection_Wait \
1011 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1012 "\n" \
1013 "Usage: wait <milliseconds>\n"
1014
1015 #define kMDNSColliderProgramSection_Loop \
1016 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1017 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1018 "\n" \
1019 "Usage: loop <non-zero count>; ... ; done\n" \
1020 "\n" \
1021 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1022 "\n" \
1023 " loop 3; wait 1000; send; done"
1024
1025 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1026
1027 static const char * gMDNSCollider_Name = NULL;
1028 static const char * gMDNSCollider_Type = NULL;
1029 static const char * gMDNSCollider_RecordData = NULL;
1030 static int gMDNSCollider_UseIPv4 = false;
1031 static int gMDNSCollider_UseIPv6 = false;
1032 static const char * gMDNSCollider_Program = NULL;
1033
1034 static CLIOption kMDNSColliderOpts[] =
1035 {
1036 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1037 StringOption( 'n', "name", &gMDNSCollider_Name, "name", "Collider's record name.", true ),
1038 StringOption( 't', "type", &gMDNSCollider_Type, "type", "Collider's record type.", true ),
1039 StringOption( 'd', "data", &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1040 StringOption( 'p', "program", &gMDNSCollider_Program, "program", "Program to execute. See Program section below.", true ),
1041 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4, "Use IPv4." ),
1042 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6, "Use IPv6." ),
1043
1044 RecordDataSection(),
1045 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro ),
1046 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes ),
1047 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send ),
1048 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait ),
1049 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop ),
1050 CLI_OPTION_END()
1051 };
1052
1053 static void MDNSColliderCmd( void );
1054
1055 //===========================================================================================================================
1056 // PIDToUUID Command Options
1057 //===========================================================================================================================
1058
1059 static int gPIDToUUID_PID = 0;
1060
1061 static CLIOption kPIDToUUIDOpts[] =
1062 {
1063 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1064 CLI_OPTION_END()
1065 };
1066
1067 //===========================================================================================================================
1068 // DNSServer Command Options
1069 //===========================================================================================================================
1070
1071 #define kDNSServerInfoText_Intro \
1072 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1073 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1074 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1075 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1076 "\n" \
1077 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1078
1079 #define kDNSServerInfoText_NameExistence \
1080 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1081 "\n" \
1082 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1083 "order, unless otherwise noted, consist of\n" \
1084 "\n" \
1085 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1086 " 2. at most one Count label;\n" \
1087 " 3. zero or more Tag labels;\n" \
1088 " 4. at most one TTL label; and\n" \
1089 " 5. at most one IPv4 or IPv6 label.\n" \
1090 "\n" \
1091 "An SRV name is defined as a name with the following form:\n" \
1092 "\n" \
1093 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1094 "\n" \
1095 "See \"SRV Names\" for details.\n"
1096
1097 #define kDNSServerInfoText_ResourceRecords \
1098 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1099 "\n" \
1100 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1101 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1102 "\n" \
1103 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1104 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1105 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1106 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1107 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1108 "\n" \
1109 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1110 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1111 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1112 "\n" \
1113 "SRV names are names of SRV records.\n" \
1114 "\n" \
1115 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1116 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1117 "AAAA records with specific TTL values.\n"
1118
1119 #define kDNSServerInfoText_AliasLabel \
1120 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1121 "\n" \
1122 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1123 "exactly N CNAME records:\n" \
1124 "\n" \
1125 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1126 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1127 " name has \"alias-(i - 1)\" as its first label.\n" \
1128 "\n" \
1129 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1130 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1131 " \"alias\" instead.\n" \
1132 "\n" \
1133 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1134 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1135 "\n" \
1136 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1137 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1138 "QNAME minus its first label.\n" \
1139 "\n" \
1140 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1141 "records:\n" \
1142 "\n" \
1143 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1144 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1145 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1146 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1147
1148 #define kDNSServerInfoText_AliasTTLLabel \
1149 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1150 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1151 "\n" \
1152 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1153 "will contain exactly N CNAME records:\n" \
1154 "\n" \
1155 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1156 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1157 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1158 "\n" \
1159 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1160 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1161 " label.\n" \
1162 "\n" \
1163 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1164 "CNAME records:\n" \
1165 "\n" \
1166 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1167 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1168 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1169
1170 #define kDNSServerInfoText_CountLabel \
1171 "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" \
1172 "an integer in [N_1, 255].\n" \
1173 "\n" \
1174 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1175 "QTYPE, then the response will contain exactly N address records:\n" \
1176 "\n" \
1177 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1178 " and whose RDATA is an address equal to a constant base address + i.\n" \
1179 "\n" \
1180 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1181 "\n" \
1182 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1183 "records:\n" \
1184 "\n" \
1185 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1186 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1187 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1188 "\n" \
1189 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1190 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1191 "\n" \
1192 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1193 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1194 "\n" \
1195 " 2. The order of the address records will be random.\n" \
1196 "\n" \
1197 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1198 "following AAAA records:\n" \
1199 "\n" \
1200 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1201 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1202 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1203 "\n" \
1204 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1205 "will contain no address records, regardless of whether it contains a Count label.\n" \
1206 "\n" \
1207 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1208 "count-1\".\n"
1209
1210 #define kDNSServerInfoText_TagLabel \
1211 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1212 "\n" \
1213 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1214 "to increase the sizes of domain names.\n"
1215
1216 #define kDNSServerInfoText_TTLLabel \
1217 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1218 "\n" \
1219 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1220 "response will have a TTL value equal to T.\n"
1221
1222 #define kDNSServerInfoText_IPv4Label \
1223 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1224
1225 #define kDNSServerInfoText_IPv6Label \
1226 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1227
1228 #define kDNSServerInfoText_SRVNames \
1229 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1230 "\n" \
1231 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1232 "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" \
1233 "the target hostname of each of the SRV name's SRV records.\n" \
1234 "\n" \
1235 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1236 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1237 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1238 "d.test. labels, whichever comes first.\n" \
1239 "\n" \
1240 "Example. A response to an SRV record query with a QNAME of\n" \
1241 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1242 "\n" \
1243 "_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" \
1244 "_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"
1245
1246 #define kDNSServerInfoText_BadUDPMode \
1247 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1248 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1249 "that's not equal to the query's message ID.\n" \
1250 "\n" \
1251 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1252 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1253 "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" \
1254 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1255 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1256
1257 static int gDNSServer_LoopbackOnly = false;
1258 static int gDNSServer_Foreground = false;
1259 static int gDNSServer_ResponseDelayMs = 0;
1260 static int gDNSServer_DefaultTTL = 60;
1261 static int gDNSServer_Port = kDNSPort;
1262 static const char * gDNSServer_DomainOverride = NULL;
1263 #if( TARGET_OS_DARWIN )
1264 static const char * gDNSServer_FollowPID = NULL;
1265 #endif
1266 static int gDNSServer_BadUDPMode = false;
1267
1268 static CLIOption kDNSServerOpts[] =
1269 {
1270 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ),
1271 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Direct log output to stdout instead of system logging." ),
1272 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1273 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1274 IntegerOption( 'p', "port", &gDNSServer_Port, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1275 StringOption( 0 , "domain", &gDNSServer_DomainOverride, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1276 #if( TARGET_OS_DARWIN )
1277 StringOption( 0 , "follow", &gDNSServer_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1278 #endif
1279 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1280
1281 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1282 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1283 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1284 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1285 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1286 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1287 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1288 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1289 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1290 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1291 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames ),
1292 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode ),
1293 CLI_OPTION_END()
1294 };
1295
1296 static void DNSServerCmd( void );
1297
1298 //===========================================================================================================================
1299 // MDNSReplier Command Options
1300 //===========================================================================================================================
1301
1302 #define kMDNSReplierPortBase 50000
1303
1304 #define kMDNSReplierInfoText_Intro \
1305 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1306 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1307 "\n" \
1308 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1309
1310 #define kMDNSReplierInfoText_Parameters \
1311 "There are five parameters that control the replier's set of authoritative records.\n" \
1312 "\n" \
1313 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1314 " parameter is specified with the --hostname option.\n" \
1315 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1316 " option.\n" \
1317 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1318 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1319 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1320 " --maxInstanceCount option.\n" \
1321 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1322 " with the --countA option.\n" \
1323 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1324 " specified with the --countAAAA option.\n"
1325
1326 #define kMDNSReplierInfoText_PTR \
1327 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1328 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1329 "\n" \
1330 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1331 "\n" \
1332 " 1. The first PTR record is defined as\n" \
1333 "\n" \
1334 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1335 " TYPE: PTR\n" \
1336 " CLASS: IN\n" \
1337 " TTL: 4500\n" \
1338 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1339 "\n" \
1340 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1341 "\n" \
1342 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1343 " TYPE: PTR\n" \
1344 " CLASS: IN\n" \
1345 " TTL: 4500\n" \
1346 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1347
1348 #define kMDNSReplierInfoText_SRV \
1349 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1350 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1351 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1352 "\n" \
1353 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1354 "\n" \
1355 " 1. The first SRV record is defined as\n" \
1356 "\n" \
1357 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1358 " TYPE: SRV\n" \
1359 " CLASS: IN\n" \
1360 " TTL: 120\n" \
1361 " RDATA:\n" \
1362 " Priority: 0\n" \
1363 " Weight: 0\n" \
1364 " Port: (50000 + L) mod 2^16\n" \
1365 " Target: <hostname>.local.\n" \
1366 "\n" \
1367 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1368 "\n" \
1369 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1370 " TYPE: SRV\n" \
1371 " CLASS: IN\n" \
1372 " TTL: 120\n" \
1373 " RDATA:\n" \
1374 " Priority: 0\n" \
1375 " Weight: 0\n" \
1376 " Port: (50000 + L) mod 2^16\n" \
1377 " Target: <hostname>-<i>.local.\n"
1378
1379 #define kMDNSReplierInfoText_TXT \
1380 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1381 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1382 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1383 "\n" \
1384 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1385 "\n" \
1386 " 1. The first TXT record is defined as\n" \
1387 "\n" \
1388 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1389 " TYPE: TXT\n" \
1390 " CLASS: IN\n" \
1391 " TTL: 4500\n" \
1392 " RDLENGTH: L\n" \
1393 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1394 "\n" \
1395 " 2. For each i in [2, N], there is one TXT record:\n" \
1396 "\n" \
1397 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1398 " TYPE: TXT\n" \
1399 " CLASS: IN\n" \
1400 " TTL: 4500\n" \
1401 " RDLENGTH: L\n" \
1402 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1403 "\n" \
1404 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1405 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1406 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1407
1408 #define kMDNSReplierInfoText_A \
1409 "The replier has exactly N_max x N_a authoritative A records:\n" \
1410 "\n" \
1411 " 1. For each j in [1, N_a], an A record is defined as\n" \
1412 "\n" \
1413 " NAME: <hostname>.local.\n" \
1414 " TYPE: A\n" \
1415 " CLASS: IN\n" \
1416 " TTL: 120\n" \
1417 " RDLENGTH: 4\n" \
1418 " RDATA: 0.0.1.<j>\n" \
1419 "\n" \
1420 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1421 "\n" \
1422 " NAME: <hostname>-<i>.local.\n" \
1423 " TYPE: A\n" \
1424 " CLASS: IN\n" \
1425 " TTL: 120\n" \
1426 " RDLENGTH: 4\n" \
1427 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1428
1429 #define kMDNSReplierInfoText_AAAA \
1430 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1431 "\n" \
1432 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1433 "\n" \
1434 " NAME: <hostname>.local.\n" \
1435 " TYPE: AAAA\n" \
1436 " CLASS: IN\n" \
1437 " TTL: 120\n" \
1438 " RDLENGTH: 16\n" \
1439 " RDATA: 2001:db8:2::1:<j>\n" \
1440 "\n" \
1441 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1442 "\n" \
1443 " NAME: <hostname>-<i>.local.\n" \
1444 " TYPE: AAAA\n" \
1445 " CLASS: IN\n" \
1446 " TTL: 120\n" \
1447 " RDLENGTH: 16\n" \
1448 " RDATA: 2001:db8:2::<i>:<j>\n"
1449
1450 #define kMDNSReplierInfoText_Responses \
1451 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1452 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1453 "separate response messages.\n"
1454
1455 static const char * gMDNSReplier_Hostname = NULL;
1456 static const char * gMDNSReplier_ServiceTypeTag = NULL;
1457 static int gMDNSReplier_MaxInstanceCount = 1000;
1458 static int gMDNSReplier_NoAdditionals = false;
1459 static int gMDNSReplier_RecordCountA = 1;
1460 static int gMDNSReplier_RecordCountAAAA = 1;
1461 static double gMDNSReplier_UnicastDropRate = 0.0;
1462 static double gMDNSReplier_MulticastDropRate = 0.0;
1463 static int gMDNSReplier_MaxDropCount = 0;
1464 static int gMDNSReplier_UseIPv4 = false;
1465 static int gMDNSReplier_UseIPv6 = false;
1466 static int gMDNSReplier_Foreground = false;
1467 static const char * gMDNSReplier_FollowPID = NULL;
1468
1469 static CLIOption kMDNSReplierOpts[] =
1470 {
1471 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1472 StringOption( 'n', "hostname", &gMDNSReplier_Hostname, "string", "Base name to use for hostnames and service instance names.", true ),
1473 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1474 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount, "count", "Maximum number of service instances. (default: 1000)", false ),
1475 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals, "When answering queries, don't include any additional records." ),
1476 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1477 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1478 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1479 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1480 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1481 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4, "Use IPv4." ),
1482 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6, "Use IPv6." ),
1483 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground, "Direct log output to stdout instead of system logging." ),
1484 #if( TARGET_OS_DARWIN )
1485 StringOption( 0 , "follow", &gMDNSReplier_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1486 #endif
1487
1488 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro ),
1489 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters ),
1490 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR ),
1491 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV ),
1492 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT ),
1493 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A ),
1494 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA ),
1495 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses ),
1496 CLI_OPTION_END()
1497 };
1498
1499 static void MDNSReplierCmd( void );
1500
1501 //===========================================================================================================================
1502 // Test Command Options
1503 //===========================================================================================================================
1504
1505 #define kTestExitStatusSection_Name "Exit Status"
1506 #define kTestExitStatusSection_Text \
1507 "This test command can exit with one of three status codes:\n" \
1508 "\n" \
1509 "0 - The test ran to completion and passed.\n" \
1510 "1 - A fatal error prevented the test from completing.\n" \
1511 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1512 "\n" \
1513 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1514 "performance.\n"
1515
1516 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1517
1518 #define kGAIPerfTestSuiteName_Basic "basic"
1519 #define kGAIPerfTestSuiteName_Advanced "advanced"
1520
1521 static const char * gGAIPerf_TestSuite = NULL;
1522 static int gGAIPerf_CallDelayMs = 10;
1523 static int gGAIPerf_ServerDelayMs = 10;
1524 static int gGAIPerf_SkipPathEvalulation = false;
1525 static int gGAIPerf_BadUDPMode = false;
1526 static int gGAIPerf_IterationCount = 100;
1527 static int gGAIPerf_IterationTimeLimitMs = 100;
1528 static const char * gGAIPerf_OutputFilePath = NULL;
1529 static const char * gGAIPerf_OutputFormat = kOutputFormatStr_JSON;
1530 static int gGAIPerf_OutputAppendNewline = false;
1531
1532 static void GAIPerfCmd( void );
1533
1534 #define kGAIPerfSectionText_TestSuiteBasic \
1535 "This test suite consists of the following three test cases:\n" \
1536 "\n" \
1537 "Test Case #1: Resolve a domain name with\n" \
1538 "\n" \
1539 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1540 "\n" \
1541 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1542 "server queries.\n" \
1543 "\n" \
1544 "Test Case #2: Resolve a domain name with\n" \
1545 "\n" \
1546 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1547 "\n" \
1548 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1549 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1550 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1551 "\n" \
1552 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1553 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1554 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1555 "\n" \
1556 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1557
1558 #define kGAIPerfSectionText_TestSuiteAdvanced \
1559 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1560 "\n" \
1561 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1562 "\n" \
1563 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1564 "\n" \
1565 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1566 "server queries.\n" \
1567 "\n" \
1568 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1569 "\n" \
1570 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1571 "\n" \
1572 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1573 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1574 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1575 "\n" \
1576 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1577 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1578 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1579 "\n" \
1580 "N_c and N_a take on the following values, depending on the value of N:\n" \
1581 "\n" \
1582 " N_c is 0 if N is in [1, 8].\n" \
1583 " N_c is 1 if N is in [9, 16].\n" \
1584 " N_c is 2 if N is in [17, 24].\n" \
1585 " N_c is 4 if N is in [25, 32].\n" \
1586 "\n" \
1587 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1588 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1589 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1590 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1591 "\n" \
1592 "Finally,\n" \
1593 "\n" \
1594 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1595
1596 static CLIOption kGAIPerfOpts[] =
1597 {
1598 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1599 "\n"
1600 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1601 "\n"
1602 ),
1603 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1604 FormatOption( 'f', "format", &gGAIPerf_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1605 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1606 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount, "count", "The number of iterations per test case. (default: 100)", false ),
1607 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1608 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1609 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1610
1611 CLI_OPTION_GROUP( "DNS Server Options" ),
1612 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1613 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1614
1615 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic ),
1616 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced ),
1617 TestExitStatusSection(),
1618 CLI_OPTION_END()
1619 };
1620
1621 static void MDNSDiscoveryTestCmd( void );
1622
1623 static int gMDNSDiscoveryTest_InstanceCount = 100;
1624 static int gMDNSDiscoveryTest_TXTSize = 100;
1625 static int gMDNSDiscoveryTest_BrowseTimeSecs = 2;
1626 static int gMDNSDiscoveryTest_FlushCache = false;
1627 static char * gMDNSDiscoveryTest_Interface = NULL;
1628 static int gMDNSDiscoveryTest_NoAdditionals = false;
1629 static int gMDNSDiscoveryTest_RecordCountA = 1;
1630 static int gMDNSDiscoveryTest_RecordCountAAAA = 1;
1631 static double gMDNSDiscoveryTest_UnicastDropRate = 0.0;
1632 static double gMDNSDiscoveryTest_MulticastDropRate = 0.0;
1633 static int gMDNSDiscoveryTest_MaxDropCount = 0;
1634 static int gMDNSDiscoveryTest_UseIPv4 = false;
1635 static int gMDNSDiscoveryTest_UseIPv6 = false;
1636 static const char * gMDNSDiscoveryTest_OutputFormat = kOutputFormatStr_JSON;
1637 static int gMDNSDiscoveryTest_OutputAppendNewline = false;
1638 static const char * gMDNSDiscoveryTest_OutputFilePath = NULL;
1639
1640 static CLIOption kMDNSDiscoveryTestOpts[] =
1641 {
1642 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount, "count", "Number of service instances to discover. (default: 100)", false ),
1643 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1644 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1645 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1646
1647 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1648 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1649 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals, "When answering queries, don't include any additional records." ),
1650 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1651 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1652 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1653 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1654 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1655 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4, "Use IPv4." ),
1656 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6, "Use IPv6." ),
1657
1658 CLI_OPTION_GROUP( "Results" ),
1659 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1660 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1661
1662 TestExitStatusSection(),
1663 CLI_OPTION_END()
1664 };
1665
1666 static void DotLocalTestCmd( void );
1667
1668 static const char * gDotLocalTest_Interface = NULL;
1669 static const char * gDotLocalTest_OutputFormat = kOutputFormatStr_JSON;
1670 static const char * gDotLocalTest_OutputFilePath = NULL;
1671
1672 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1673 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1674 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1675 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1676 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1677 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1678 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1679
1680 #define kDotLocalTestSectionText_Description \
1681 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1682 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1683 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1684 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1685 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1686 "then the mdnsreplier's base hostname is test.local.\n" \
1687 "\n" \
1688 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1689 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1690 "\n" \
1691 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1692 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1693 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1694 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1695 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1696 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1697 "\n" \
1698 "Each subtest runs for five seconds.\n"
1699
1700 static CLIOption kDotLocalTestOpts[] =
1701 {
1702 StringOption( 'i', "interface", &gDotLocalTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1703
1704 CLI_OPTION_GROUP( "Results" ),
1705 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1706 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1707
1708 CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1709 TestExitStatusSection(),
1710 CLI_OPTION_END()
1711 };
1712
1713 static void ProbeConflictTestCmd( void );
1714
1715 static const char * gProbeConflictTest_Interface = NULL;
1716 static int gProbeConflictTest_UseComputerName = false;
1717 static const char * gProbeConflictTest_OutputFormat = kOutputFormatStr_JSON;
1718 static const char * gProbeConflictTest_OutputFilePath = NULL;
1719
1720 static CLIOption kProbeConflictTestOpts[] =
1721 {
1722 StringOption( 'i', "interface", &gProbeConflictTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1723 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1724
1725 CLI_OPTION_GROUP( "Results" ),
1726 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat, "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1727 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1728
1729 TestExitStatusSection(),
1730 CLI_OPTION_END()
1731 };
1732
1733 static void RegistrationTestCmd( void );
1734
1735 static int gRegistrationTest_BATSEnvironment = false;
1736 static const char * gRegistrationTest_OutputFormat = kOutputFormatStr_JSON;
1737 static const char * gRegistrationTest_OutputFilePath = NULL;
1738
1739 static CLIOption kRegistrationTestOpts[] =
1740 {
1741 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment, "Informs the test that it's running in a BATS environment.",
1742 "\n"
1743 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1744 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1745 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1746 "\n"
1747 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1748 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1749 ),
1750 CLI_OPTION_GROUP( "Results" ),
1751 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1752 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1753
1754 TestExitStatusSection(),
1755 CLI_OPTION_END()
1756 };
1757
1758 static void ExpensiveConstrainedTestCmd( void );
1759
1760 static const char * gExpensiveConstrainedTest_Interface = NULL;
1761 static const char * gExpensiveConstrainedTest_Name = NULL;
1762 static Boolean gExpensiveConstrainedTest_DenyExpensive = false;
1763 static Boolean gExpensiveConstrainedTest_DenyConstrained = false;
1764 static Boolean gExpensiveConstrainedTest_StartFromExpensive = false;
1765 static int gExpensiveConstrainedTest_ProtocolIPv4 = false;
1766 static int gExpensiveConstrainedTest_ProtocolIPv6 = false;
1767 static const char * gExpensiveConstrainedTest_OutputFormat = kOutputFormatStr_JSON;
1768 static const char * gExpensiveConstrainedTest_OutputFilePath = NULL;
1769
1770 static CLIOption kExpensiveConstrainedTestOpts[] =
1771 {
1772 CLI_OPTION_GROUP( "Results" ),
1773 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1774 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1775
1776 TestExitStatusSection(),
1777 CLI_OPTION_END()
1778 };
1779
1780 static CLIOption kTestOpts[] =
1781 {
1782 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1783 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts, "Tests mDNS service discovery for correctness.", false ),
1784 Command( "dotlocal", DotLocalTestCmd, kDotLocalTestOpts, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1785 Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts, "Tests various probing conflict scenarios.", false ),
1786 Command( "registration", RegistrationTestCmd, kRegistrationTestOpts, "Tests service registrations.", false ),
1787 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd, kExpensiveConstrainedTestOpts, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1788 CLI_OPTION_END()
1789 };
1790
1791 //===========================================================================================================================
1792 // SSDP Command Options
1793 //===========================================================================================================================
1794
1795 static int gSSDPDiscover_MX = 1;
1796 static const char * gSSDPDiscover_ST = "ssdp:all";
1797 static int gSSDPDiscover_ReceiveSecs = 1;
1798 static int gSSDPDiscover_UseIPv4 = false;
1799 static int gSSDPDiscover_UseIPv6 = false;
1800 static int gSSDPDiscover_Verbose = false;
1801
1802 static CLIOption kSSDPDiscoverOpts[] =
1803 {
1804 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1805 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1806 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1807 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1808 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1809 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1810 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
1811 CLI_OPTION_END()
1812 };
1813
1814 static void SSDPDiscoverCmd( void );
1815
1816 static CLIOption kSSDPOpts[] =
1817 {
1818 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
1819 CLI_OPTION_END()
1820 };
1821
1822 #if( TARGET_OS_DARWIN )
1823 //===========================================================================================================================
1824 // res_query Command Options
1825 //===========================================================================================================================
1826
1827 static void ResQueryCmd( void );
1828
1829 static const char * gResQuery_Name = NULL;
1830 static const char * gResQuery_Type = NULL;
1831 static const char * gResQuery_Class = NULL;
1832 static int gResQuery_UseLibInfo = false;
1833
1834 static CLIOption kResQueryOpts[] =
1835 {
1836 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
1837 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1838 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1839 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
1840 CLI_OPTION_END()
1841 };
1842
1843 //===========================================================================================================================
1844 // dns_query Command Options
1845 //===========================================================================================================================
1846
1847 static void ResolvDNSQueryCmd( void );
1848
1849 static const char * gResolvDNSQuery_Name = NULL;
1850 static const char * gResolvDNSQuery_Type = NULL;
1851 static const char * gResolvDNSQuery_Class = NULL;
1852 static const char * gResolvDNSQuery_Path = NULL;
1853
1854 static CLIOption kResolvDNSQueryOpts[] =
1855 {
1856 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
1857 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1858 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1859 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1860 CLI_OPTION_END()
1861 };
1862
1863 //===========================================================================================================================
1864 // CFHost Command Options
1865 //===========================================================================================================================
1866
1867 static void CFHostCmd( void );
1868
1869 static const char * gCFHost_Name = NULL;
1870 static int gCFHost_WaitSecs = 0;
1871
1872 static CLIOption kCFHostOpts[] =
1873 {
1874 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
1875 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1876 CLI_OPTION_END()
1877 };
1878
1879 static CLIOption kLegacyOpts[] =
1880 {
1881 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1882 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
1883 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
1884 CLI_OPTION_END()
1885 };
1886
1887 //===========================================================================================================================
1888 // DNSConfigAdd Command Options
1889 //===========================================================================================================================
1890
1891 static void DNSConfigAddCmd( void );
1892
1893 static CFStringRef gDNSConfigAdd_ID = NULL;
1894 static char ** gDNSConfigAdd_IPAddrArray = NULL;
1895 static size_t gDNSConfigAdd_IPAddrCount = 0;
1896 static char ** gDNSConfigAdd_DomainArray = NULL;
1897 static size_t gDNSConfigAdd_DomainCount = 0;
1898 static const char * gDNSConfigAdd_Interface = NULL;
1899
1900 static CLIOption kDNSConfigAddOpts[] =
1901 {
1902 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
1903 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1904 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1905 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
1906
1907 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1908 CLI_OPTION_END()
1909 };
1910
1911 //===========================================================================================================================
1912 // DNSConfigRemove Command Options
1913 //===========================================================================================================================
1914
1915 static void DNSConfigRemoveCmd( void );
1916
1917 static CFStringRef gDNSConfigRemove_ID = NULL;
1918
1919 static CLIOption kDNSConfigRemoveOpts[] =
1920 {
1921 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
1922
1923 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1924 CLI_OPTION_END()
1925 };
1926
1927 static CLIOption kDNSConfigOpts[] =
1928 {
1929 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1930 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1931 CLI_OPTION_END()
1932 };
1933
1934 //===========================================================================================================================
1935 // XPCSend
1936 //===========================================================================================================================
1937
1938 static void XPCSendCmd( void );
1939
1940 static const char * gXPCSend_ServiceName = NULL;
1941 static const char * gXPCSend_MessageStr = NULL;
1942
1943 static const char kXPCSendMessageSection_Name[] = "Message Argument";
1944 static const char kXPCSendMessageSection_Text[] =
1945 "XPC messages are described as a string using the following syntax.\n"
1946 "\n"
1947 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
1948 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
1949 "currently supported XPC types.\n"
1950 "\n"
1951 "Arrays begin with a '[' and end with a ']'.\n"
1952 "\n"
1953 "The following non-container XPC types are supported:\n"
1954 "\n"
1955 "Type Syntax Example\n"
1956 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
1957 "data data:<hex string> data:C0000201\n"
1958 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
1959 "string string:<string> string:hello\\ world\n"
1960 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
1961 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
1962 "\n"
1963 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
1964 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
1965
1966 static CLIOption kXPCSendOpts[] =
1967 {
1968 StringOption( 's', "service", &gXPCSend_ServiceName, "service name", "XPC service name.", true ),
1969 StringOption( 'm', "message", &gXPCSend_MessageStr, "message", "XPC message as a string.", true ),
1970
1971 CLI_SECTION( kXPCSendMessageSection_Name, kXPCSendMessageSection_Text ),
1972 CLI_OPTION_END()
1973 };
1974 #endif // TARGET_OS_DARWIN
1975
1976 #if ( MDNSRESPONDER_PROJECT )
1977 //===========================================================================================================================
1978 // InterfaceMonitor
1979 //===========================================================================================================================
1980
1981 static void InterfaceMonitorCmd( void );
1982
1983 static CLIOption kInterfaceMonitorOpts[] =
1984 {
1985 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1986 CLI_OPTION_END()
1987 };
1988
1989 //===========================================================================================================================
1990 // DNSProxy
1991 //===========================================================================================================================
1992
1993 static void DNSProxyCmd( void );
1994
1995 static char ** gDNSProxy_InputInterfaces = NULL;
1996 static size_t gDNSProxy_InputInterfaceCount = 0;
1997 static const char * gDNSProxy_OutputInterface = NULL;
1998
1999 static CLIOption kDNSProxyOpts[] =
2000 {
2001 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces, &gDNSProxy_InputInterfaceCount, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2002 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2003 CLI_OPTION_END()
2004 };
2005 #endif // MDNSRESPONDER_PROJECT
2006
2007 //===========================================================================================================================
2008 // Command Table
2009 //===========================================================================================================================
2010
2011 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2012
2013 static void BrowseCmd( void );
2014 static void GetAddrInfoCmd( void );
2015 static void QueryRecordCmd( void );
2016 static void RegisterCmd( void );
2017 static void RegisterRecordCmd( void );
2018 static void ResolveCmd( void );
2019 static void ReconfirmCmd( void );
2020 static void GetAddrInfoPOSIXCmd( void );
2021 static void ReverseLookupCmd( void );
2022 static void PortMappingCmd( void );
2023 static void BrowseAllCmd( void );
2024 static void GetAddrInfoStressCmd( void );
2025 static void DNSQueryCmd( void );
2026 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2027 static void DNSCryptCmd( void );
2028 #endif
2029 static void MDNSQueryCmd( void );
2030 static void PIDToUUIDCmd( void );
2031 static void DaemonVersionCmd( void );
2032
2033 static CLIOption kGlobalOpts[] =
2034 {
2035 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2036 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2037 CLI_OPTION_HELP(),
2038
2039 // Common commands.
2040
2041 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2042 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2043 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2044 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2045 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2046 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2047 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2048 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2049 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2050 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2051 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2052
2053 // Uncommon commands.
2054
2055 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2056 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2057 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2058 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2059 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2060 #endif
2061 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2062 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2063 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2064 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2065 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2066 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2067 Command( "ssdp", NULL, kSSDPOpts, "Simple Service Discovery Protocol (SSDP).", true ),
2068 #if( TARGET_OS_DARWIN )
2069 Command( "legacy", NULL, kLegacyOpts, "Legacy DNS API.", true ),
2070 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2071 Command( "xpcsend", XPCSendCmd, kXPCSendOpts, "Sends a message to an XPC service.", true ),
2072 #endif
2073 #if ( MDNSRESPONDER_PROJECT )
2074 Command( "interfaceMonitor", InterfaceMonitorCmd, kInterfaceMonitorOpts, "mDNSResponder's interface monitor.", true ),
2075 Command( "dnsproxy", DNSProxyCmd, kDNSProxyOpts, "Enables mDNSResponder's DNS proxy.", true ),
2076 #endif
2077 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2078
2079 CLI_COMMAND_HELP(),
2080 CLI_OPTION_END()
2081 };
2082
2083 //===========================================================================================================================
2084 // Helper Prototypes
2085 //===========================================================================================================================
2086
2087 #define kExitReason_OneShotDone "one-shot done"
2088 #define kExitReason_ReceivedResponse "received response"
2089 #define kExitReason_SIGINT "interrupt signal"
2090 #define kExitReason_Timeout "timeout"
2091 #define kExitReason_TimeLimit "time limit"
2092
2093 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2094
2095 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2096
2097 typedef enum
2098 {
2099 kConnectionType_None = 0,
2100 kConnectionType_Normal = 1,
2101 kConnectionType_DelegatePID = 2,
2102 kConnectionType_DelegateUUID = 3
2103
2104 } ConnectionType;
2105
2106 typedef struct
2107 {
2108 ConnectionType type;
2109 union
2110 {
2111 int32_t pid;
2112 uint8_t uuid[ 16 ];
2113
2114 } delegate;
2115
2116 } ConnectionDesc;
2117
2118 static OSStatus
2119 CreateConnectionFromArgString(
2120 const char * inString,
2121 dispatch_queue_t inQueue,
2122 DNSServiceRef * outSDRef,
2123 ConnectionDesc * outDesc );
2124 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2125 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2126 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2127 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2128
2129 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2130
2131 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2132 static const char * RecordTypeToString( unsigned int inValue );
2133
2134 static OSStatus
2135 DNSRecordDataToString(
2136 const void * inRDataPtr,
2137 size_t inRDataLen,
2138 unsigned int inRDataType,
2139 const void * inMsgPtr,
2140 size_t inMsgLen,
2141 char ** outString );
2142
2143 static OSStatus
2144 DNSMessageToText(
2145 const uint8_t * inMsgPtr,
2146 size_t inMsgLen,
2147 Boolean inIsMDNS,
2148 Boolean inPrintRaw,
2149 char ** outText );
2150
2151 static OSStatus
2152 WriteDNSQueryMessage(
2153 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2154 uint16_t inMsgID,
2155 uint16_t inFlags,
2156 const char * inQName,
2157 uint16_t inQType,
2158 uint16_t inQClass,
2159 size_t * outMsgLen );
2160
2161 // Dispatch helpers
2162
2163 typedef void ( *DispatchHandler )( void *inContext );
2164
2165 static OSStatus
2166 DispatchSignalSourceCreate(
2167 int inSignal,
2168 DispatchHandler inEventHandler,
2169 void * inContext,
2170 dispatch_source_t * outSource );
2171 static OSStatus
2172 DispatchSocketSourceCreate(
2173 SocketRef inSock,
2174 dispatch_source_type_t inType,
2175 dispatch_queue_t inQueue,
2176 DispatchHandler inEventHandler,
2177 DispatchHandler inCancelHandler,
2178 void * inContext,
2179 dispatch_source_t * outSource );
2180
2181 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2182 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2183
2184 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2185 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2186
2187 static OSStatus
2188 DispatchTimerCreate(
2189 dispatch_time_t inStart,
2190 uint64_t inIntervalNs,
2191 uint64_t inLeewayNs,
2192 dispatch_queue_t inQueue,
2193 DispatchHandler inEventHandler,
2194 DispatchHandler inCancelHandler,
2195 void * inContext,
2196 dispatch_source_t * outTimer );
2197
2198 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2199 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2200
2201 static OSStatus
2202 DispatchProcessMonitorCreate(
2203 pid_t inPID,
2204 unsigned long inFlags,
2205 dispatch_queue_t inQueue,
2206 DispatchHandler inEventHandler,
2207 DispatchHandler inCancelHandler,
2208 void * inContext,
2209 dispatch_source_t * outMonitor );
2210
2211 static const char * ServiceTypeDescription( const char *inName );
2212
2213 typedef struct
2214 {
2215 SocketRef sock; // Socket.
2216 void * userContext; // User context.
2217 int32_t refCount; // Reference count.
2218
2219 } SocketContext;
2220
2221 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
2222 static SocketContext * SocketContextRetain( SocketContext *inContext );
2223 static void SocketContextRelease( SocketContext *inContext );
2224 static void SocketContextCancelHandler( void *inContext );
2225
2226 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2227
2228 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2229 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2230 #if( TARGET_OS_DARWIN )
2231 static int64_t _StringToInt64( const char *inString, OSStatus *outError );
2232 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError );
2233 static pid_t _StringToPID( const char *inString, OSStatus *outError );
2234 static OSStatus
2235 _ParseEscapedString(
2236 const char * inSrc,
2237 const char * inEnd,
2238 const char * inDelimiters,
2239 char * inBufPtr,
2240 size_t inBufLen,
2241 size_t * outCopiedLen,
2242 size_t * outActualLen,
2243 const char ** outPtr );
2244 #endif
2245 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2246 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2247 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2248 #if( TARGET_OS_DARWIN )
2249 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2250 #endif
2251 static OSStatus
2252 _ServerSocketOpenEx2(
2253 int inFamily,
2254 int inType,
2255 int inProtocol,
2256 const void * inAddr,
2257 int inPort,
2258 int * outPort,
2259 int inRcvBufSize,
2260 Boolean inNoPortReuse,
2261 SocketRef * outSock );
2262
2263 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2264 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2265
2266 static OSStatus
2267 CreateMulticastSocket(
2268 const struct sockaddr * inAddr,
2269 int inPort,
2270 const char * inIfName,
2271 uint32_t inIfIndex,
2272 Boolean inJoin,
2273 int * outPort,
2274 SocketRef * outSock );
2275
2276 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2277 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2278 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2279 static OSStatus CheckRootUser( void );
2280 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... );
2281 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2282 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2283 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2284 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2285 static OSStatus
2286 CreateNSECRecordData(
2287 const uint8_t * inNextDomainName,
2288 uint8_t ** outPtr,
2289 size_t * outLen,
2290 unsigned int inTypeCount,
2291 ... );
2292 static OSStatus
2293 AppendSOARecord(
2294 DataBuffer * inDB,
2295 const uint8_t * inNamePtr,
2296 size_t inNameLen,
2297 uint16_t inType,
2298 uint16_t inClass,
2299 uint32_t inTTL,
2300 const uint8_t * inMName,
2301 const uint8_t * inRName,
2302 uint32_t inSerial,
2303 uint32_t inRefresh,
2304 uint32_t inRetry,
2305 uint32_t inExpire,
2306 uint32_t inMinimumTTL,
2307 size_t * outLen );
2308 static OSStatus
2309 CreateSOARecordData(
2310 const uint8_t * inMName,
2311 const uint8_t * inRName,
2312 uint32_t inSerial,
2313 uint32_t inRefresh,
2314 uint32_t inRetry,
2315 uint32_t inExpire,
2316 uint32_t inMinimumTTL,
2317 uint8_t ** outPtr,
2318 size_t * outLen );
2319 static OSStatus
2320 _DataBuffer_AppendDNSQuestion(
2321 DataBuffer * inDB,
2322 const uint8_t * inNamePtr,
2323 size_t inNameLen,
2324 uint16_t inType,
2325 uint16_t inClass );
2326 static OSStatus
2327 _DataBuffer_AppendDNSRecord(
2328 DataBuffer * inDB,
2329 const uint8_t * inNamePtr,
2330 size_t inNameLen,
2331 uint16_t inType,
2332 uint16_t inClass,
2333 uint32_t inTTL,
2334 const uint8_t * inRDataPtr,
2335 size_t inRDataLen );
2336 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2337
2338 typedef struct MDNSInterfaceItem MDNSInterfaceItem;
2339 struct MDNSInterfaceItem
2340 {
2341 MDNSInterfaceItem * next;
2342 char * ifName;
2343 uint32_t ifIndex;
2344 Boolean hasIPv4;
2345 Boolean hasIPv6;
2346 Boolean isAWDL;
2347 Boolean isWiFi;
2348 };
2349
2350 typedef enum
2351 {
2352 kMDNSInterfaceSubset_All = 0, // All mDNS-capable interfaces.
2353 kMDNSInterfaceSubset_AWDL = 1, // All mDNS-capable AWDL interfaces.
2354 kMDNSInterfaceSubset_NonAWDL = 2 // All mDNS-capable non-AWDL iterfaces.
2355
2356 } MDNSInterfaceSubset;
2357
2358 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2359 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2360 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2361 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2362
2363 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2364 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName );
2365 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName );
2366 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2367
2368 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2369 static OSStatus
2370 _StringToIPv4Address(
2371 const char * inStr,
2372 StringToIPAddressFlags inFlags,
2373 uint32_t * outIP,
2374 int * outPort,
2375 uint32_t * outSubnet,
2376 uint32_t * outRouter,
2377 const char ** outStr );
2378 static void _StringArray_Free( char **inArray, size_t inCount );
2379 static OSStatus
2380 _StringToIPv6Address(
2381 const char * inStr,
2382 StringToIPAddressFlags inFlags,
2383 uint8_t outIPv6[ 16 ],
2384 uint32_t * outScope,
2385 int * outPort,
2386 int * outPrefix,
2387 const char ** outStr );
2388 static Boolean
2389 _ParseQuotedEscapedString(
2390 const char * inSrc,
2391 const char * inEnd,
2392 const char * inDelimiters,
2393 char * inBuf,
2394 size_t inMaxLen,
2395 size_t * outCopiedLen,
2396 size_t * outTotalLen,
2397 const char ** outSrc );
2398 static void * _memdup( const void *inPtr, size_t inLen );
2399 static int _memicmp( const void *inP1, const void *inP2, size_t inLen );
2400 static uint32_t _FNV1( const void *inData, size_t inSize );
2401
2402 #define Unused( X ) (void)(X)
2403
2404 //===========================================================================================================================
2405 // MDNSCollider
2406 //===========================================================================================================================
2407
2408 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2409
2410 typedef uint32_t MDNSColliderProtocols;
2411 #define kMDNSColliderProtocol_None 0
2412 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2413 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2414
2415 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2416
2417 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2418 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2419 static void MDNSColliderStop( MDNSColliderRef inCollider );
2420 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2421 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2422 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2423 static void
2424 MDNSColliderSetStopHandler(
2425 MDNSColliderRef inCollider,
2426 MDNSColliderStopHandler_f inStopHandler,
2427 void * inStopContext );
2428 static OSStatus
2429 MDNSColliderSetRecord(
2430 MDNSColliderRef inCollider,
2431 const uint8_t * inName,
2432 uint16_t inType,
2433 const void * inRDataPtr,
2434 size_t inRDataLen );
2435 static CFTypeID MDNSColliderGetTypeID( void );
2436
2437 //===========================================================================================================================
2438 // ServiceBrowser
2439 //===========================================================================================================================
2440
2441 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2442 typedef struct ServiceBrowserResults ServiceBrowserResults;
2443 typedef struct SBRDomain SBRDomain;
2444 typedef struct SBRServiceType SBRServiceType;
2445 typedef struct SBRServiceInstance SBRServiceInstance;
2446 typedef struct SBRIPAddress SBRIPAddress;
2447
2448 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2449
2450 struct ServiceBrowserResults
2451 {
2452 SBRDomain * domainList; // List of domains in which services were found.
2453 };
2454
2455 struct SBRDomain
2456 {
2457 SBRDomain * next; // Next domain in list.
2458 char * name; // Name of domain represented by this object.
2459 SBRServiceType * typeList; // List of service types in this domain.
2460 };
2461
2462 struct SBRServiceType
2463 {
2464 SBRServiceType * next; // Next service type in list.
2465 char * name; // Name of service type represented by this object.
2466 SBRServiceInstance * instanceList; // List of service instances of this service type.
2467 };
2468
2469 struct SBRServiceInstance
2470 {
2471 SBRServiceInstance * next; // Next service instance in list.
2472 char * name; // Name of service instance represented by this object.
2473 char * hostname; // Target from service instance's SRV record.
2474 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2475 uint16_t port; // Port from service instance's SRV record.
2476 uint8_t * txtPtr; // Service instance's TXT record data.
2477 size_t txtLen; // Service instance's TXT record data length.
2478 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2479 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2480 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2481 };
2482
2483 struct SBRIPAddress
2484 {
2485 SBRIPAddress * next; // Next IP address in list.
2486 sockaddr_ip sip; // IPv4 or IPv6 address.
2487 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2488 };
2489
2490 static CFTypeID ServiceBrowserGetTypeID( void );
2491 static OSStatus
2492 ServiceBrowserCreate(
2493 dispatch_queue_t inQueue,
2494 uint32_t inInterfaceIndex,
2495 const char * inDomain,
2496 unsigned int inBrowseTimeSecs,
2497 Boolean inIncludeAWDL,
2498 ServiceBrowserRef * outBrowser );
2499 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2500 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2501 static void
2502 ServiceBrowserSetCallback(
2503 ServiceBrowserRef inBrowser,
2504 ServiceBrowserCallback_f inCallback,
2505 void * inContext );
2506 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2507 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2508
2509 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2510
2511 //===========================================================================================================================
2512 // main
2513 //===========================================================================================================================
2514
2515 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2516 static int \
2517 _PrintFExtension ## NAME ## Handler( \
2518 PrintFContext * inContext, \
2519 PrintFFormat * inFormat, \
2520 PrintFVAList * inArgs, \
2521 void * inUserContext )
2522
2523 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
2524 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
2525 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
2526 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
2527
2528 int main( int argc, const char **argv )
2529 {
2530 OSStatus err;
2531
2532 // Route DebugServices logging output to stderr.
2533
2534 dlog_control( "DebugServices:output=file;stderr" );
2535
2536 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler, NULL );
2537 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler, NULL );
2538 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler, NULL );
2539 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler, NULL );
2540 CLIInit( argc, argv );
2541 err = CLIParse( kGlobalOpts, kCLIFlags_None );
2542 if( err ) exit( 1 );
2543
2544 return( gExitCode );
2545 }
2546
2547 //===========================================================================================================================
2548 // VersionOptionCallback
2549 //===========================================================================================================================
2550
2551 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
2552 {
2553 const char * srcVers;
2554 #if( MDNSRESPONDER_PROJECT )
2555 char srcStr[ 16 ];
2556 #endif
2557
2558 Unused( inOption );
2559 Unused( inArg );
2560 Unused( inUnset );
2561
2562 #if( MDNSRESPONDER_PROJECT )
2563 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
2564 #else
2565 srcVers = DNSSDUTIL_SOURCE_VERSION;
2566 #endif
2567 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
2568
2569 return( kEndingErr );
2570 }
2571
2572 //===========================================================================================================================
2573 // BrowseCmd
2574 //===========================================================================================================================
2575
2576 typedef struct BrowseResolveOp BrowseResolveOp;
2577
2578 struct BrowseResolveOp
2579 {
2580 BrowseResolveOp * next; // Next resolve operation in list.
2581 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2582 char * fullName; // Full name of the service to resolve.
2583 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2584 };
2585
2586 typedef struct
2587 {
2588 DNSServiceRef mainRef; // Main sdRef for shared connection.
2589 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
2590 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
2591 const char * domain; // Domain for DNSServiceBrowse operation(s).
2592 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
2593 char ** serviceTypes; // Array of service types to browse for.
2594 size_t serviceTypesCount; // Count of array of service types to browse for.
2595 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
2596 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
2597 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
2598 Boolean printedHeader; // True if results header has been printed.
2599 Boolean doResolve; // True if service instances are to be resolved.
2600 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
2601
2602 } BrowseContext;
2603
2604 static void BrowsePrintPrologue( const BrowseContext *inContext );
2605 static void BrowseContextFree( BrowseContext *inContext );
2606 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
2607 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
2608 static void DNSSD_API
2609 BrowseCallback(
2610 DNSServiceRef inSDRef,
2611 DNSServiceFlags inFlags,
2612 uint32_t inInterfaceIndex,
2613 DNSServiceErrorType inError,
2614 const char * inName,
2615 const char * inRegType,
2616 const char * inDomain,
2617 void * inContext );
2618 static void DNSSD_API
2619 BrowseResolveCallback(
2620 DNSServiceRef inSDRef,
2621 DNSServiceFlags inFlags,
2622 uint32_t inInterfaceIndex,
2623 DNSServiceErrorType inError,
2624 const char * inFullName,
2625 const char * inHostname,
2626 uint16_t inPort,
2627 uint16_t inTXTLen,
2628 const unsigned char * inTXTPtr,
2629 void * inContext );
2630 static void DNSSD_API
2631 BrowseQueryRecordCallback(
2632 DNSServiceRef inSDRef,
2633 DNSServiceFlags inFlags,
2634 uint32_t inInterfaceIndex,
2635 DNSServiceErrorType inError,
2636 const char * inFullName,
2637 uint16_t inType,
2638 uint16_t inClass,
2639 uint16_t inRDataLen,
2640 const void * inRDataPtr,
2641 uint32_t inTTL,
2642 void * inContext );
2643
2644 static void BrowseCmd( void )
2645 {
2646 OSStatus err;
2647 size_t i;
2648 BrowseContext * context = NULL;
2649 dispatch_source_t signalSource = NULL;
2650 int useMainConnection;
2651
2652 // Set up SIGINT handler.
2653
2654 signal( SIGINT, SIG_IGN );
2655 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2656 require_noerr( err, exit );
2657 dispatch_resume( signalSource );
2658
2659 // Create context.
2660
2661 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
2662 require_action( context, exit, err = kNoMemoryErr );
2663
2664 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
2665 require_action( context->opRefs, exit, err = kNoMemoryErr );
2666 context->opRefsCount = gBrowse_ServiceTypesCount;
2667
2668 // Check command parameters.
2669
2670 if( gBrowse_TimeLimitSecs < 0 )
2671 {
2672 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
2673 err = kParamErr;
2674 goto exit;
2675 }
2676
2677 // Create main connection.
2678
2679 if( gConnectionOpt )
2680 {
2681 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2682 require_noerr_quiet( err, exit );
2683 useMainConnection = true;
2684 }
2685 else
2686 {
2687 useMainConnection = false;
2688 }
2689
2690 // Get flags.
2691
2692 context->flags = GetDNSSDFlagsFromOpts();
2693 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2694
2695 // Get interface.
2696
2697 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2698 require_noerr_quiet( err, exit );
2699
2700 // Set remaining parameters.
2701
2702 context->serviceTypes = gBrowse_ServiceTypes;
2703 context->serviceTypesCount = gBrowse_ServiceTypesCount;
2704 context->domain = gBrowse_Domain;
2705 context->doResolve = gBrowse_DoResolve ? true : false;
2706 context->timeLimitSecs = gBrowse_TimeLimitSecs;
2707 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
2708
2709 // Print prologue.
2710
2711 BrowsePrintPrologue( context );
2712
2713 // Start operation(s).
2714
2715 for( i = 0; i < context->serviceTypesCount; ++i )
2716 {
2717 DNSServiceRef sdRef;
2718
2719 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2720 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
2721 BrowseCallback, context );
2722 require_noerr( err, exit );
2723
2724 context->opRefs[ i ] = sdRef;
2725 if( !useMainConnection )
2726 {
2727 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
2728 require_noerr( err, exit );
2729 }
2730 }
2731
2732 // Set time limit.
2733
2734 if( context->timeLimitSecs > 0 )
2735 {
2736 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2737 kExitReason_TimeLimit, Exit );
2738 }
2739 dispatch_main();
2740
2741 exit:
2742 dispatch_source_forget( &signalSource );
2743 if( context ) BrowseContextFree( context );
2744 if( err ) exit( 1 );
2745 }
2746
2747 //===========================================================================================================================
2748 // BrowsePrintPrologue
2749 //===========================================================================================================================
2750
2751 static void BrowsePrintPrologue( const BrowseContext *inContext )
2752 {
2753 const int timeLimitSecs = inContext->timeLimitSecs;
2754 const char * const * ptr = (const char **) inContext->serviceTypes;
2755 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
2756 char ifName[ kInterfaceNameBufLen ];
2757
2758 InterfaceIndexToName( inContext->ifIndex, ifName );
2759
2760 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2761 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2762 FPrintF( stdout, "Service types: %s", *ptr++ );
2763 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
2764 FPrintF( stdout, "\n" );
2765 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2766 FPrintF( stdout, "Time limit: " );
2767 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2768 else FPrintF( stdout, "∞\n" );
2769 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2770 FPrintF( stdout, "---\n" );
2771 }
2772
2773 //===========================================================================================================================
2774 // BrowseContextFree
2775 //===========================================================================================================================
2776
2777 static void BrowseContextFree( BrowseContext *inContext )
2778 {
2779 size_t i;
2780
2781 for( i = 0; i < inContext->opRefsCount; ++i )
2782 {
2783 DNSServiceForget( &inContext->opRefs[ i ] );
2784 }
2785 if( inContext->serviceTypes )
2786 {
2787 _StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
2788 inContext->serviceTypes = NULL;
2789 inContext->serviceTypesCount = 0;
2790 }
2791 DNSServiceForget( &inContext->mainRef );
2792 free( inContext );
2793 }
2794
2795 //===========================================================================================================================
2796 // BrowseResolveOpCreate
2797 //===========================================================================================================================
2798
2799 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
2800 {
2801 OSStatus err;
2802 BrowseResolveOp * resolveOp;
2803
2804 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
2805 require_action( resolveOp, exit, err = kNoMemoryErr );
2806
2807 resolveOp->fullName = strdup( inFullName );
2808 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
2809
2810 resolveOp->interfaceIndex = inInterfaceIndex;
2811
2812 *outOp = resolveOp;
2813 resolveOp = NULL;
2814 err = kNoErr;
2815
2816 exit:
2817 if( resolveOp ) BrowseResolveOpFree( resolveOp );
2818 return( err );
2819 }
2820
2821 //===========================================================================================================================
2822 // BrowseResolveOpFree
2823 //===========================================================================================================================
2824
2825 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
2826 {
2827 DNSServiceForget( &inOp->sdRef );
2828 ForgetMem( &inOp->fullName );
2829 free( inOp );
2830 }
2831
2832 //===========================================================================================================================
2833 // BrowseCallback
2834 //===========================================================================================================================
2835
2836 static void DNSSD_API
2837 BrowseCallback(
2838 DNSServiceRef inSDRef,
2839 DNSServiceFlags inFlags,
2840 uint32_t inInterfaceIndex,
2841 DNSServiceErrorType inError,
2842 const char * inName,
2843 const char * inRegType,
2844 const char * inDomain,
2845 void * inContext )
2846 {
2847 BrowseContext * const context = (BrowseContext *) inContext;
2848 OSStatus err;
2849 BrowseResolveOp * newOp = NULL;
2850 BrowseResolveOp ** p;
2851 char fullName[ kDNSServiceMaxDomainName ];
2852 struct timeval now;
2853
2854 Unused( inSDRef );
2855
2856 gettimeofday( &now, NULL );
2857
2858 err = inError;
2859 require_noerr( err, exit );
2860
2861 if( !context->printedHeader )
2862 {
2863 FPrintF( stdout, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2864 context->printedHeader = true;
2865 }
2866 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2867 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2868
2869 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2870
2871 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2872 require_noerr( err, exit );
2873
2874 if( inFlags & kDNSServiceFlagsAdd )
2875 {
2876 DNSServiceRef sdRef;
2877 DNSServiceFlags flags;
2878
2879 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2880 require_noerr( err, exit );
2881
2882 if( context->mainRef )
2883 {
2884 sdRef = context->mainRef;
2885 flags = kDNSServiceFlagsShareConnection;
2886 }
2887 else
2888 {
2889 flags = 0;
2890 }
2891 if( context->doResolve )
2892 {
2893 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2894 NULL );
2895 require_noerr( err, exit );
2896 }
2897 else
2898 {
2899 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2900 BrowseQueryRecordCallback, NULL );
2901 require_noerr( err, exit );
2902 }
2903
2904 newOp->sdRef = sdRef;
2905 if( !context->mainRef )
2906 {
2907 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2908 require_noerr( err, exit );
2909 }
2910 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2911 *p = newOp;
2912 newOp = NULL;
2913 }
2914 else
2915 {
2916 BrowseResolveOp * resolveOp;
2917
2918 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2919 {
2920 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
2921 {
2922 break;
2923 }
2924 }
2925 if( resolveOp )
2926 {
2927 *p = resolveOp->next;
2928 BrowseResolveOpFree( resolveOp );
2929 }
2930 }
2931
2932 exit:
2933 if( newOp ) BrowseResolveOpFree( newOp );
2934 if( err ) exit( 1 );
2935 }
2936
2937 //===========================================================================================================================
2938 // BrowseQueryRecordCallback
2939 //===========================================================================================================================
2940
2941 static void DNSSD_API
2942 BrowseQueryRecordCallback(
2943 DNSServiceRef inSDRef,
2944 DNSServiceFlags inFlags,
2945 uint32_t inInterfaceIndex,
2946 DNSServiceErrorType inError,
2947 const char * inFullName,
2948 uint16_t inType,
2949 uint16_t inClass,
2950 uint16_t inRDataLen,
2951 const void * inRDataPtr,
2952 uint32_t inTTL,
2953 void * inContext )
2954 {
2955 OSStatus err;
2956 struct timeval now;
2957
2958 Unused( inSDRef );
2959 Unused( inClass );
2960 Unused( inTTL );
2961 Unused( inContext );
2962
2963 gettimeofday( &now, NULL );
2964
2965 err = inError;
2966 require_noerr( err, exit );
2967 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
2968
2969 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2970 &now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
2971 (size_t) inRDataLen );
2972
2973 exit:
2974 if( err ) exit( 1 );
2975 }
2976
2977 //===========================================================================================================================
2978 // BrowseResolveCallback
2979 //===========================================================================================================================
2980
2981 static void DNSSD_API
2982 BrowseResolveCallback(
2983 DNSServiceRef inSDRef,
2984 DNSServiceFlags inFlags,
2985 uint32_t inInterfaceIndex,
2986 DNSServiceErrorType inError,
2987 const char * inFullName,
2988 const char * inHostname,
2989 uint16_t inPort,
2990 uint16_t inTXTLen,
2991 const unsigned char * inTXTPtr,
2992 void * inContext )
2993 {
2994 struct timeval now;
2995 char errorStr[ 64 ];
2996
2997 Unused( inSDRef );
2998 Unused( inFlags );
2999 Unused( inContext );
3000
3001 gettimeofday( &now, NULL );
3002
3003 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3004
3005 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3006 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3007 if( inTXTLen == 1 )
3008 {
3009 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3010 }
3011 else
3012 {
3013 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3014 }
3015 }
3016
3017 //===========================================================================================================================
3018 // GetAddrInfoCmd
3019 //===========================================================================================================================
3020
3021 typedef struct
3022 {
3023 DNSServiceRef mainRef; // Main sdRef for shared connection.
3024 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3025 const char * name; // Hostname to resolve.
3026 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3027 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3028 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3029 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3030 Boolean printedHeader; // True if the results header has been printed.
3031 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3032 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3033 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3034
3035 } GetAddrInfoContext;
3036
3037 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3038 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3039 static void DNSSD_API
3040 GetAddrInfoCallback(
3041 DNSServiceRef inSDRef,
3042 DNSServiceFlags inFlags,
3043 uint32_t inInterfaceIndex,
3044 DNSServiceErrorType inError,
3045 const char * inHostname,
3046 const struct sockaddr * inSockAddr,
3047 uint32_t inTTL,
3048 void * inContext );
3049
3050 static void GetAddrInfoCmd( void )
3051 {
3052 OSStatus err;
3053 DNSServiceRef sdRef;
3054 GetAddrInfoContext * context = NULL;
3055 dispatch_source_t signalSource = NULL;
3056 int useMainConnection;
3057
3058 // Set up SIGINT handler.
3059
3060 signal( SIGINT, SIG_IGN );
3061 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3062 require_noerr( err, exit );
3063 dispatch_resume( signalSource );
3064
3065 // Check command parameters.
3066
3067 if( gGetAddrInfo_TimeLimitSecs < 0 )
3068 {
3069 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3070 err = kParamErr;
3071 goto exit;
3072 }
3073
3074 // Create context.
3075
3076 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3077 require_action( context, exit, err = kNoMemoryErr );
3078
3079 // Create main connection.
3080
3081 if( gConnectionOpt )
3082 {
3083 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3084 require_noerr_quiet( err, exit );
3085 useMainConnection = true;
3086 }
3087 else
3088 {
3089 useMainConnection = false;
3090 }
3091
3092 // Get flags.
3093
3094 context->flags = GetDNSSDFlagsFromOpts();
3095 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3096
3097 // Get interface.
3098
3099 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3100 require_noerr_quiet( err, exit );
3101
3102 // Set remaining parameters.
3103
3104 context->name = gGetAddrInfo_Name;
3105 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3106 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3107 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3108 if( gGetAddrInfo_OneShot )
3109 {
3110 context->oneShotMode = true;
3111 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3112 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3113 }
3114
3115 // Print prologue.
3116
3117 GetAddrInfoPrintPrologue( context );
3118
3119 // Start operation.
3120
3121 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3122 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3123 GetAddrInfoCallback, context );
3124 require_noerr( err, exit );
3125
3126 context->opRef = sdRef;
3127 if( !useMainConnection )
3128 {
3129 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3130 require_noerr( err, exit );
3131 }
3132
3133 // Set time limit.
3134
3135 if( context->timeLimitSecs > 0 )
3136 {
3137 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3138 kExitReason_TimeLimit, Exit );
3139 }
3140 dispatch_main();
3141
3142 exit:
3143 dispatch_source_forget( &signalSource );
3144 if( context ) GetAddrInfoContextFree( context );
3145 if( err ) exit( 1 );
3146 }
3147
3148 //===========================================================================================================================
3149 // GetAddrInfoPrintPrologue
3150 //===========================================================================================================================
3151
3152 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3153 {
3154 const int timeLimitSecs = inContext->timeLimitSecs;
3155 char ifName[ kInterfaceNameBufLen ];
3156
3157 InterfaceIndexToName( inContext->ifIndex, ifName );
3158
3159 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3160 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3161 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3162 FPrintF( stdout, "Name: %s\n", inContext->name );
3163 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3164 FPrintF( stdout, "Time limit: " );
3165 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3166 else FPrintF( stdout, "∞\n" );
3167 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3168 FPrintF( stdout, "---\n" );
3169 }
3170
3171 //===========================================================================================================================
3172 // GetAddrInfoContextFree
3173 //===========================================================================================================================
3174
3175 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3176 {
3177 DNSServiceForget( &inContext->opRef );
3178 DNSServiceForget( &inContext->mainRef );
3179 free( inContext );
3180 }
3181
3182 //===========================================================================================================================
3183 // GetAddrInfoCallback
3184 //===========================================================================================================================
3185
3186 static void DNSSD_API
3187 GetAddrInfoCallback(
3188 DNSServiceRef inSDRef,
3189 DNSServiceFlags inFlags,
3190 uint32_t inInterfaceIndex,
3191 DNSServiceErrorType inError,
3192 const char * inHostname,
3193 const struct sockaddr * inSockAddr,
3194 uint32_t inTTL,
3195 void * inContext )
3196 {
3197 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3198 struct timeval now;
3199 OSStatus err;
3200 const char * addrStr;
3201 char addrStrBuf[ kSockAddrStringMaxSize ];
3202
3203 Unused( inSDRef );
3204
3205 gettimeofday( &now, NULL );
3206
3207 switch( inError )
3208 {
3209 case kDNSServiceErr_NoError:
3210 case kDNSServiceErr_NoSuchRecord:
3211 err = kNoErr;
3212 break;
3213
3214 case kDNSServiceErr_Timeout:
3215 Exit( kExitReason_Timeout );
3216
3217 default:
3218 err = inError;
3219 goto exit;
3220 }
3221
3222 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3223 {
3224 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3225 err = kTypeErr;
3226 goto exit;
3227 }
3228
3229 if( !inError )
3230 {
3231 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3232 require_noerr( err, exit );
3233 addrStr = addrStrBuf;
3234 }
3235 else
3236 {
3237 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3238 }
3239
3240 if( !context->printedHeader )
3241 {
3242 FPrintF( stdout, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3243 context->printedHeader = true;
3244 }
3245 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3246 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3247
3248 if( context->oneShotMode )
3249 {
3250 if( inFlags & kDNSServiceFlagsAdd )
3251 {
3252 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3253 else context->needIPv6 = false;
3254 }
3255 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3256 {
3257 Exit( kExitReason_OneShotDone );
3258 }
3259 }
3260
3261 exit:
3262 if( err ) exit( 1 );
3263 }
3264
3265 //===========================================================================================================================
3266 // QueryRecordCmd
3267 //===========================================================================================================================
3268
3269 typedef struct
3270 {
3271 DNSServiceRef mainRef; // Main sdRef for shared connection.
3272 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3273 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3274 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3275 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3276 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3277 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3278 Boolean printedHeader; // True if the results header was printed.
3279 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3280 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3281 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3282
3283 } QueryRecordContext;
3284
3285 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3286 static void QueryRecordContextFree( QueryRecordContext *inContext );
3287 static void DNSSD_API
3288 QueryRecordCallback(
3289 DNSServiceRef inSDRef,
3290 DNSServiceFlags inFlags,
3291 uint32_t inInterfaceIndex,
3292 DNSServiceErrorType inError,
3293 const char * inFullName,
3294 uint16_t inType,
3295 uint16_t inClass,
3296 uint16_t inRDataLen,
3297 const void * inRDataPtr,
3298 uint32_t inTTL,
3299 void * inContext );
3300
3301 static void QueryRecordCmd( void )
3302 {
3303 OSStatus err;
3304 DNSServiceRef sdRef;
3305 QueryRecordContext * context = NULL;
3306 dispatch_source_t signalSource = NULL;
3307 int useMainConnection;
3308
3309 // Set up SIGINT handler.
3310
3311 signal( SIGINT, SIG_IGN );
3312 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3313 require_noerr( err, exit );
3314 dispatch_resume( signalSource );
3315
3316 // Create context.
3317
3318 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3319 require_action( context, exit, err = kNoMemoryErr );
3320
3321 // Check command parameters.
3322
3323 if( gQueryRecord_TimeLimitSecs < 0 )
3324 {
3325 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3326 err = kParamErr;
3327 goto exit;
3328 }
3329
3330 // Create main connection.
3331
3332 if( gConnectionOpt )
3333 {
3334 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3335 require_noerr_quiet( err, exit );
3336 useMainConnection = true;
3337 }
3338 else
3339 {
3340 useMainConnection = false;
3341 }
3342
3343 // Get flags.
3344
3345 context->flags = GetDNSSDFlagsFromOpts();
3346 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3347
3348 // Get interface.
3349
3350 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3351 require_noerr_quiet( err, exit );
3352
3353 // Get record type.
3354
3355 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3356 require_noerr( err, exit );
3357
3358 // Set remaining parameters.
3359
3360 context->recordName = gQueryRecord_Name;
3361 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3362 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3363 context->printRawRData = gQueryRecord_RawRData ? true : false;
3364
3365 // Print prologue.
3366
3367 QueryRecordPrintPrologue( context );
3368
3369 // Start operation.
3370
3371 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3372 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3373 kDNSServiceClass_IN, QueryRecordCallback, context );
3374 require_noerr( err, exit );
3375
3376 context->opRef = sdRef;
3377 if( !useMainConnection )
3378 {
3379 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3380 require_noerr( err, exit );
3381 }
3382
3383 // Set time limit.
3384
3385 if( context->timeLimitSecs > 0 )
3386 {
3387 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3388 Exit );
3389 }
3390 dispatch_main();
3391
3392 exit:
3393 dispatch_source_forget( &signalSource );
3394 if( context ) QueryRecordContextFree( context );
3395 if( err ) exit( 1 );
3396 }
3397
3398 //===========================================================================================================================
3399 // QueryRecordContextFree
3400 //===========================================================================================================================
3401
3402 static void QueryRecordContextFree( QueryRecordContext *inContext )
3403 {
3404 DNSServiceForget( &inContext->opRef );
3405 DNSServiceForget( &inContext->mainRef );
3406 free( inContext );
3407 }
3408
3409 //===========================================================================================================================
3410 // QueryRecordPrintPrologue
3411 //===========================================================================================================================
3412
3413 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3414 {
3415 const int timeLimitSecs = inContext->timeLimitSecs;
3416 char ifName[ kInterfaceNameBufLen ];
3417
3418 InterfaceIndexToName( inContext->ifIndex, ifName );
3419
3420 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3421 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3422 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3423 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3424 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3425 FPrintF( stdout, "Time limit: " );
3426 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3427 else FPrintF( stdout, "∞\n" );
3428 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3429 FPrintF( stdout, "---\n" );
3430
3431 }
3432
3433 //===========================================================================================================================
3434 // QueryRecordCallback
3435 //===========================================================================================================================
3436
3437 static void DNSSD_API
3438 QueryRecordCallback(
3439 DNSServiceRef inSDRef,
3440 DNSServiceFlags inFlags,
3441 uint32_t inInterfaceIndex,
3442 DNSServiceErrorType inError,
3443 const char * inFullName,
3444 uint16_t inType,
3445 uint16_t inClass,
3446 uint16_t inRDataLen,
3447 const void * inRDataPtr,
3448 uint32_t inTTL,
3449 void * inContext )
3450 {
3451 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3452 struct timeval now;
3453 OSStatus err;
3454 char * rdataStr = NULL;
3455
3456 Unused( inSDRef );
3457
3458 gettimeofday( &now, NULL );
3459
3460 switch( inError )
3461 {
3462 case kDNSServiceErr_NoError:
3463 case kDNSServiceErr_NoSuchRecord:
3464 err = kNoErr;
3465 break;
3466
3467 case kDNSServiceErr_Timeout:
3468 Exit( kExitReason_Timeout );
3469
3470 default:
3471 err = inError;
3472 goto exit;
3473 }
3474
3475 if( inError != kDNSServiceErr_NoSuchRecord )
3476 {
3477 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
3478 if( !rdataStr )
3479 {
3480 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
3481 require_action( rdataStr, exit, err = kNoMemoryErr );
3482 }
3483 }
3484
3485 if( !context->printedHeader )
3486 {
3487 FPrintF( stdout, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3488 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3489 context->printedHeader = true;
3490 }
3491 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3492 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
3493 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL,
3494 rdataStr ? rdataStr : kNoSuchRecordStr );
3495
3496 if( context->oneShotMode )
3497 {
3498 if( ( inFlags & kDNSServiceFlagsAdd ) &&
3499 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
3500 {
3501 context->gotRecord = true;
3502 }
3503 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
3504 }
3505
3506 exit:
3507 FreeNullSafe( rdataStr );
3508 if( err ) exit( 1 );
3509 }
3510
3511 //===========================================================================================================================
3512 // RegisterCmd
3513 //===========================================================================================================================
3514
3515 typedef struct
3516 {
3517 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
3518 uint8_t * dataPtr; // Record data.
3519 size_t dataLen; // Record data length.
3520 uint32_t ttl; // Record TTL value.
3521 uint16_t type; // Record type.
3522
3523 } ExtraRecord;
3524
3525 typedef struct
3526 {
3527 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
3528 const char * name; // Service name argument for DNSServiceRegister().
3529 const char * type; // Service type argument for DNSServiceRegister().
3530 const char * domain; // Domain in which advertise the service.
3531 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
3532 size_t txtLen; // Service TXT record data len.
3533 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
3534 size_t extraRecordsCount; // Number of extra records.
3535 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
3536 size_t updateTXTLen; // Length of record data for TXT record update.
3537 uint32_t updateTTL; // TTL of updated TXT record.
3538 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
3539 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
3540 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
3541 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3542 uint16_t port; // Service instance's port number.
3543 Boolean printedHeader; // True if results header was printed.
3544 Boolean didRegister; // True if service was registered.
3545
3546 } RegisterContext;
3547
3548 static void RegisterPrintPrologue( const RegisterContext *inContext );
3549 static void RegisterContextFree( RegisterContext *inContext );
3550 static void DNSSD_API
3551 RegisterCallback(
3552 DNSServiceRef inSDRef,
3553 DNSServiceFlags inFlags,
3554 DNSServiceErrorType inError,
3555 const char * inName,
3556 const char * inType,
3557 const char * inDomain,
3558 void * inContext );
3559 static void RegisterUpdate( void *inContext );
3560
3561 static void RegisterCmd( void )
3562 {
3563 OSStatus err;
3564 RegisterContext * context = NULL;
3565 dispatch_source_t signalSource = NULL;
3566
3567 // Set up SIGINT handler.
3568
3569 signal( SIGINT, SIG_IGN );
3570 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3571 require_noerr( err, exit );
3572 dispatch_resume( signalSource );
3573
3574 // Create context.
3575
3576 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
3577 require_action( context, exit, err = kNoMemoryErr );
3578
3579 // Check command parameters.
3580
3581 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
3582 {
3583 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
3584 err = kParamErr;
3585 goto exit;
3586 }
3587
3588 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
3589 {
3590 FPrintF( stderr, "There are missing additional record parameters.\n" );
3591 err = kParamErr;
3592 goto exit;
3593 }
3594
3595 // Get flags.
3596
3597 context->flags = GetDNSSDFlagsFromOpts();
3598
3599 // Get interface index.
3600
3601 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3602 require_noerr_quiet( err, exit );
3603
3604 // Get TXT record data.
3605
3606 if( gRegister_TXT )
3607 {
3608 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
3609 require_noerr_quiet( err, exit );
3610 }
3611
3612 // Set remaining parameters.
3613
3614 context->name = gRegister_Name;
3615 context->type = gRegister_Type;
3616 context->domain = gRegister_Domain;
3617 context->port = (uint16_t) gRegister_Port;
3618 context->lifetimeMs = gRegister_LifetimeMs;
3619
3620 if( gAddRecord_TypesCount > 0 )
3621 {
3622 size_t i;
3623
3624 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
3625 require_action( context, exit, err = kNoMemoryErr );
3626 context->extraRecordsCount = gAddRecord_TypesCount;
3627
3628 for( i = 0; i < gAddRecord_TypesCount; ++i )
3629 {
3630 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
3631
3632 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
3633 require_noerr( err, exit );
3634
3635 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
3636 if( err )
3637 {
3638 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
3639 err = kParamErr;
3640 goto exit;
3641 }
3642
3643 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
3644 require_noerr_quiet( err, exit );
3645 }
3646 }
3647
3648 if( gUpdateRecord_Data )
3649 {
3650 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
3651 require_noerr_quiet( err, exit );
3652
3653 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
3654 context->updateDelayMs = gUpdateRecord_DelayMs;
3655 }
3656
3657 // Print prologue.
3658
3659 RegisterPrintPrologue( context );
3660
3661 // Start operation.
3662
3663 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
3664 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
3665 RegisterCallback, context );
3666 ForgetMem( &context->txtPtr );
3667 require_noerr( err, exit );
3668
3669 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3670 require_noerr( err, exit );
3671
3672 dispatch_main();
3673
3674 exit:
3675 dispatch_source_forget( &signalSource );
3676 if( context ) RegisterContextFree( context );
3677 if( err ) exit( 1 );
3678 }
3679
3680 //===========================================================================================================================
3681 // RegisterPrintPrologue
3682 //===========================================================================================================================
3683
3684 static void RegisterPrintPrologue( const RegisterContext *inContext )
3685 {
3686 size_t i;
3687 int infinite;
3688 char ifName[ kInterfaceNameBufLen ];
3689
3690 InterfaceIndexToName( inContext->ifIndex, ifName );
3691
3692 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3693 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3694 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
3695 FPrintF( stdout, "Type: %s\n", inContext->type );
3696 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3697 FPrintF( stdout, "Port: %u\n", inContext->port );
3698 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
3699 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3700 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3701 if( inContext->updateTXTPtr )
3702 {
3703 FPrintF( stdout, "\nUpdate record:\n" );
3704 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
3705 FPrintF( stdout, " TTL: %u%?s\n",
3706 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3707 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
3708 }
3709 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
3710 for( i = 0; i < inContext->extraRecordsCount; ++i )
3711 {
3712 const ExtraRecord * record = &inContext->extraRecords[ i ];
3713
3714 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
3715 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
3716 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
3717 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
3718 }
3719 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3720 FPrintF( stdout, "---\n" );
3721 }
3722
3723 //===========================================================================================================================
3724 // RegisterContextFree
3725 //===========================================================================================================================
3726
3727 static void RegisterContextFree( RegisterContext *inContext )
3728 {
3729 ExtraRecord * record;
3730 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
3731
3732 DNSServiceForget( &inContext->opRef );
3733 ForgetMem( &inContext->txtPtr );
3734 for( record = inContext->extraRecords; record < end; ++record )
3735 {
3736 check( !record->recordRef );
3737 ForgetMem( &record->dataPtr );
3738 }
3739 ForgetMem( &inContext->extraRecords );
3740 ForgetMem( &inContext->updateTXTPtr );
3741 free( inContext );
3742 }
3743
3744 //===========================================================================================================================
3745 // RegisterCallback
3746 //===========================================================================================================================
3747
3748 static void DNSSD_API
3749 RegisterCallback(
3750 DNSServiceRef inSDRef,
3751 DNSServiceFlags inFlags,
3752 DNSServiceErrorType inError,
3753 const char * inName,
3754 const char * inType,
3755 const char * inDomain,
3756 void * inContext )
3757 {
3758 RegisterContext * const context = (RegisterContext *) inContext;
3759 OSStatus err;
3760 struct timeval now;
3761
3762 Unused( inSDRef );
3763
3764 gettimeofday( &now, NULL );
3765
3766 if( !context->printedHeader )
3767 {
3768 FPrintF( stdout, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3769 context->printedHeader = true;
3770 }
3771 FPrintF( stdout, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
3772
3773 require_noerr_action_quiet( inError, exit, err = inError );
3774
3775 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
3776 {
3777 context->didRegister = true;
3778 if( context->updateTXTPtr )
3779 {
3780 if( context->updateDelayMs > 0 )
3781 {
3782 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3783 context, RegisterUpdate );
3784 }
3785 else
3786 {
3787 RegisterUpdate( context );
3788 }
3789 }
3790 if( context->extraRecordsCount > 0 )
3791 {
3792 ExtraRecord * record;
3793 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
3794
3795 for( record = context->extraRecords; record < end; ++record )
3796 {
3797 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
3798 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
3799 require_noerr( err, exit );
3800 }
3801 }
3802 if( context->lifetimeMs == 0 )
3803 {
3804 Exit( kExitReason_TimeLimit );
3805 }
3806 else if( context->lifetimeMs > 0 )
3807 {
3808 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3809 kExitReason_TimeLimit, Exit );
3810 }
3811 }
3812 err = kNoErr;
3813
3814 exit:
3815 if( err ) exit( 1 );
3816 }
3817
3818 //===========================================================================================================================
3819 // RegisterUpdate
3820 //===========================================================================================================================
3821
3822 static void RegisterUpdate( void *inContext )
3823 {
3824 OSStatus err;
3825 RegisterContext * const context = (RegisterContext *) inContext;
3826
3827 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
3828 context->updateTTL );
3829 require_noerr( err, exit );
3830
3831 exit:
3832 if( err ) exit( 1 );
3833 }
3834
3835 //===========================================================================================================================
3836 // RegisterRecordCmd
3837 //===========================================================================================================================
3838
3839 typedef struct
3840 {
3841 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
3842 DNSRecordRef recordRef; // Registered record reference.
3843 const char * recordName; // Name of resource record.
3844 uint8_t * dataPtr; // Pointer to resource record data.
3845 size_t dataLen; // Length of resource record data.
3846 uint32_t ttl; // TTL value of resource record in seconds.
3847 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3848 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3849 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3850 uint16_t recordType; // Resource record type.
3851 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3852 size_t updateDataLen; // Length of data for record update.
3853 uint32_t updateTTL; // TTL for updated record.
3854 int updateDelayMs; // Post-registration record update delay in milliseconds.
3855 Boolean didRegister; // True if the record was registered.
3856
3857 } RegisterRecordContext;
3858
3859 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3860 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3861 static void DNSSD_API
3862 RegisterRecordCallback(
3863 DNSServiceRef inSDRef,
3864 DNSRecordRef inRecordRef,
3865 DNSServiceFlags inFlags,
3866 DNSServiceErrorType inError,
3867 void * inContext );
3868 static void RegisterRecordUpdate( void *inContext );
3869
3870 static void RegisterRecordCmd( void )
3871 {
3872 OSStatus err;
3873 RegisterRecordContext * context = NULL;
3874 dispatch_source_t signalSource = NULL;
3875
3876 // Set up SIGINT handler.
3877
3878 signal( SIGINT, SIG_IGN );
3879 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3880 require_noerr( err, exit );
3881 dispatch_resume( signalSource );
3882
3883 // Create context.
3884
3885 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3886 require_action( context, exit, err = kNoMemoryErr );
3887
3888 // Create connection.
3889
3890 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3891 require_noerr_quiet( err, exit );
3892
3893 // Get flags.
3894
3895 context->flags = GetDNSSDFlagsFromOpts();
3896
3897 // Get interface.
3898
3899 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3900 require_noerr_quiet( err, exit );
3901
3902 // Get record type.
3903
3904 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3905 require_noerr( err, exit );
3906
3907 // Get record data.
3908
3909 if( gRegisterRecord_Data )
3910 {
3911 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3912 require_noerr_quiet( err, exit );
3913 }
3914
3915 // Set remaining parameters.
3916
3917 context->recordName = gRegisterRecord_Name;
3918 context->ttl = (uint32_t) gRegisterRecord_TTL;
3919 context->lifetimeMs = gRegisterRecord_LifetimeMs;
3920
3921 // Get update data.
3922
3923 if( gRegisterRecord_UpdateData )
3924 {
3925 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
3926 require_noerr_quiet( err, exit );
3927
3928 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
3929 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
3930 }
3931
3932 // Print prologue.
3933
3934 RegisterRecordPrintPrologue( context );
3935
3936 // Start operation.
3937
3938 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
3939 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
3940 context->ttl, RegisterRecordCallback, context );
3941 if( err )
3942 {
3943 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
3944 goto exit;
3945 }
3946
3947 dispatch_main();
3948
3949 exit:
3950 dispatch_source_forget( &signalSource );
3951 if( context ) RegisterRecordContextFree( context );
3952 if( err ) exit( 1 );
3953 }
3954
3955 //===========================================================================================================================
3956 // RegisterRecordPrintPrologue
3957 //===========================================================================================================================
3958
3959 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
3960 {
3961 int infinite;
3962 char ifName[ kInterfaceNameBufLen ];
3963
3964 InterfaceIndexToName( inContext->ifIndex, ifName );
3965
3966 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3967 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3968 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3969 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3970 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
3971 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
3972 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3973 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3974 if( inContext->updateDataPtr )
3975 {
3976 FPrintF( stdout, "\nUpdate record:\n" );
3977 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
3978 FPrintF( stdout, " TTL: %u%?s\n",
3979 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3980 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
3981 }
3982 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3983 FPrintF( stdout, "---\n" );
3984 }
3985
3986 //===========================================================================================================================
3987 // RegisterRecordContextFree
3988 //===========================================================================================================================
3989
3990 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
3991 {
3992 DNSServiceForget( &inContext->conRef );
3993 ForgetMem( &inContext->dataPtr );
3994 ForgetMem( &inContext->updateDataPtr );
3995 free( inContext );
3996 }
3997
3998 //===========================================================================================================================
3999 // RegisterRecordCallback
4000 //===========================================================================================================================
4001
4002 static void
4003 RegisterRecordCallback(
4004 DNSServiceRef inSDRef,
4005 DNSRecordRef inRecordRef,
4006 DNSServiceFlags inFlags,
4007 DNSServiceErrorType inError,
4008 void * inContext )
4009 {
4010 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4011 struct timeval now;
4012
4013 Unused( inSDRef );
4014 Unused( inRecordRef );
4015 Unused( inFlags );
4016 Unused( context );
4017
4018 gettimeofday( &now, NULL );
4019 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4020
4021 if( !context->didRegister && !inError )
4022 {
4023 context->didRegister = true;
4024 if( context->updateDataPtr )
4025 {
4026 if( context->updateDelayMs > 0 )
4027 {
4028 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4029 context, RegisterRecordUpdate );
4030 }
4031 else
4032 {
4033 RegisterRecordUpdate( context );
4034 }
4035 }
4036 if( context->lifetimeMs == 0 )
4037 {
4038 Exit( kExitReason_TimeLimit );
4039 }
4040 else if( context->lifetimeMs > 0 )
4041 {
4042 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4043 kExitReason_TimeLimit, Exit );
4044 }
4045 }
4046 }
4047
4048 //===========================================================================================================================
4049 // RegisterRecordUpdate
4050 //===========================================================================================================================
4051
4052 static void RegisterRecordUpdate( void *inContext )
4053 {
4054 OSStatus err;
4055 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4056
4057 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4058 context->updateDataPtr, context->updateTTL );
4059 require_noerr( err, exit );
4060
4061 exit:
4062 if( err ) exit( 1 );
4063 }
4064
4065 //===========================================================================================================================
4066 // ResolveCmd
4067 //===========================================================================================================================
4068
4069 typedef struct
4070 {
4071 DNSServiceRef mainRef; // Main sdRef for shared connections.
4072 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4073 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4074 const char * name; // Service name argument for DNSServiceResolve().
4075 const char * type; // Service type argument for DNSServiceResolve().
4076 const char * domain; // Domain argument for DNSServiceResolve().
4077 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4078 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4079
4080 } ResolveContext;
4081
4082 static void ResolvePrintPrologue( const ResolveContext *inContext );
4083 static void ResolveContextFree( ResolveContext *inContext );
4084 static void DNSSD_API
4085 ResolveCallback(
4086 DNSServiceRef inSDRef,
4087 DNSServiceFlags inFlags,
4088 uint32_t inInterfaceIndex,
4089 DNSServiceErrorType inError,
4090 const char * inFullName,
4091 const char * inHostname,
4092 uint16_t inPort,
4093 uint16_t inTXTLen,
4094 const unsigned char * inTXTPtr,
4095 void * inContext );
4096
4097 static void ResolveCmd( void )
4098 {
4099 OSStatus err;
4100 DNSServiceRef sdRef;
4101 ResolveContext * context = NULL;
4102 dispatch_source_t signalSource = NULL;
4103 int useMainConnection;
4104
4105 // Set up SIGINT handler.
4106
4107 signal( SIGINT, SIG_IGN );
4108 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4109 require_noerr( err, exit );
4110 dispatch_resume( signalSource );
4111
4112 // Create context.
4113
4114 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4115 require_action( context, exit, err = kNoMemoryErr );
4116
4117 // Check command parameters.
4118
4119 if( gResolve_TimeLimitSecs < 0 )
4120 {
4121 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4122 err = kParamErr;
4123 goto exit;
4124 }
4125
4126 // Create main connection.
4127
4128 if( gConnectionOpt )
4129 {
4130 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4131 require_noerr_quiet( err, exit );
4132 useMainConnection = true;
4133 }
4134 else
4135 {
4136 useMainConnection = false;
4137 }
4138
4139 // Get flags.
4140
4141 context->flags = GetDNSSDFlagsFromOpts();
4142 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4143
4144 // Get interface index.
4145
4146 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4147 require_noerr_quiet( err, exit );
4148
4149 // Set remaining parameters.
4150
4151 context->name = gResolve_Name;
4152 context->type = gResolve_Type;
4153 context->domain = gResolve_Domain;
4154 context->timeLimitSecs = gResolve_TimeLimitSecs;
4155
4156 // Print prologue.
4157
4158 ResolvePrintPrologue( context );
4159
4160 // Start operation.
4161
4162 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4163 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4164 ResolveCallback, NULL );
4165 require_noerr( err, exit );
4166
4167 context->opRef = sdRef;
4168 if( !useMainConnection )
4169 {
4170 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4171 require_noerr( err, exit );
4172 }
4173
4174 // Set time limit.
4175
4176 if( context->timeLimitSecs > 0 )
4177 {
4178 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4179 kExitReason_TimeLimit, Exit );
4180 }
4181 dispatch_main();
4182
4183 exit:
4184 dispatch_source_forget( &signalSource );
4185 if( context ) ResolveContextFree( context );
4186 if( err ) exit( 1 );
4187 }
4188
4189 //===========================================================================================================================
4190 // ReconfirmCmd
4191 //===========================================================================================================================
4192
4193 static void ReconfirmCmd( void )
4194 {
4195 OSStatus err;
4196 uint8_t * rdataPtr = NULL;
4197 size_t rdataLen = 0;
4198 DNSServiceFlags flags;
4199 uint32_t ifIndex;
4200 uint16_t type, class;
4201 char ifName[ kInterfaceNameBufLen ];
4202
4203 // Get flags.
4204
4205 flags = GetDNSSDFlagsFromOpts();
4206
4207 // Get interface index.
4208
4209 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4210 require_noerr_quiet( err, exit );
4211
4212 // Get record type.
4213
4214 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4215 require_noerr( err, exit );
4216
4217 // Get record data.
4218
4219 if( gReconfirmRecord_Data )
4220 {
4221 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4222 require_noerr_quiet( err, exit );
4223 }
4224
4225 // Get record class.
4226
4227 if( gReconfirmRecord_Class )
4228 {
4229 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4230 require_noerr( err, exit );
4231 }
4232 else
4233 {
4234 class = kDNSServiceClass_IN;
4235 }
4236
4237 // Print prologue.
4238
4239 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4240 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4241 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4242 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4243 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4244 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4245 FPrintF( stdout, "---\n" );
4246
4247 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4248 FPrintF( stdout, "Error: %#m\n", err );
4249
4250 exit:
4251 FreeNullSafe( rdataPtr );
4252 if( err ) exit( 1 );
4253 }
4254
4255 //===========================================================================================================================
4256 // ResolvePrintPrologue
4257 //===========================================================================================================================
4258
4259 static void ResolvePrintPrologue( const ResolveContext *inContext )
4260 {
4261 const int timeLimitSecs = inContext->timeLimitSecs;
4262 char ifName[ kInterfaceNameBufLen ];
4263
4264 InterfaceIndexToName( inContext->ifIndex, ifName );
4265
4266 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4267 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4268 FPrintF( stdout, "Name: %s\n", inContext->name );
4269 FPrintF( stdout, "Type: %s\n", inContext->type );
4270 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4271 FPrintF( stdout, "Time limit: " );
4272 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4273 else FPrintF( stdout, "∞\n" );
4274 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4275 FPrintF( stdout, "---\n" );
4276 }
4277
4278 //===========================================================================================================================
4279 // ResolveContextFree
4280 //===========================================================================================================================
4281
4282 static void ResolveContextFree( ResolveContext *inContext )
4283 {
4284 DNSServiceForget( &inContext->opRef );
4285 DNSServiceForget( &inContext->mainRef );
4286 free( inContext );
4287 }
4288
4289 //===========================================================================================================================
4290 // ResolveCallback
4291 //===========================================================================================================================
4292
4293 static void DNSSD_API
4294 ResolveCallback(
4295 DNSServiceRef inSDRef,
4296 DNSServiceFlags inFlags,
4297 uint32_t inInterfaceIndex,
4298 DNSServiceErrorType inError,
4299 const char * inFullName,
4300 const char * inHostname,
4301 uint16_t inPort,
4302 uint16_t inTXTLen,
4303 const unsigned char * inTXTPtr,
4304 void * inContext )
4305 {
4306 struct timeval now;
4307 char errorStr[ 64 ];
4308
4309 Unused( inSDRef );
4310 Unused( inFlags );
4311 Unused( inContext );
4312
4313 gettimeofday( &now, NULL );
4314
4315 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4316
4317 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4318 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4319 if( inTXTLen == 1 )
4320 {
4321 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4322 }
4323 else
4324 {
4325 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4326 }
4327 }
4328
4329 //===========================================================================================================================
4330 // GetAddrInfoPOSIXCmd
4331 //===========================================================================================================================
4332
4333 #define AddressFamilyStr( X ) ( \
4334 ( (X) == AF_INET ) ? "inet" : \
4335 ( (X) == AF_INET6 ) ? "inet6" : \
4336 ( (X) == AF_UNSPEC ) ? "unspec" : \
4337 "???" )
4338
4339 typedef struct
4340 {
4341 unsigned int flag;
4342 const char * str;
4343
4344 } FlagStringPair;
4345
4346 #define CaseFlagStringify( X ) { (X), # X }
4347
4348 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4349 {
4350 #if( defined( AI_UNUSABLE ) )
4351 CaseFlagStringify( AI_UNUSABLE ),
4352 #endif
4353 CaseFlagStringify( AI_NUMERICSERV ),
4354 CaseFlagStringify( AI_V4MAPPED ),
4355 CaseFlagStringify( AI_ADDRCONFIG ),
4356 #if( defined( AI_V4MAPPED_CFG ) )
4357 CaseFlagStringify( AI_V4MAPPED_CFG ),
4358 #endif
4359 CaseFlagStringify( AI_ALL ),
4360 CaseFlagStringify( AI_NUMERICHOST ),
4361 CaseFlagStringify( AI_CANONNAME ),
4362 CaseFlagStringify( AI_PASSIVE ),
4363 { 0, NULL }
4364 };
4365
4366 static void GetAddrInfoPOSIXCmd( void )
4367 {
4368 OSStatus err;
4369 struct addrinfo hints;
4370 struct timeval now;
4371 const struct addrinfo * addrInfo;
4372 struct addrinfo * addrInfoList = NULL;
4373 const FlagStringPair * pair;
4374
4375 memset( &hints, 0, sizeof( hints ) );
4376 hints.ai_socktype = SOCK_STREAM;
4377
4378 // Set hints address family.
4379
4380 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4381 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4382 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4383 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4384 else
4385 {
4386 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4387 err = kParamErr;
4388 goto exit;
4389 }
4390
4391 // Set hints flags.
4392
4393 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4394 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4395 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4396 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4397 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4398 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4399 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4400 #if( defined( AI_V4MAPPED_CFG ) )
4401 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4402 #endif
4403 #if( defined( AI_DEFAULT ) )
4404 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4405 #endif
4406 #if( defined( AI_UNUSABLE ) )
4407 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4408 #endif
4409
4410 // Print prologue.
4411
4412 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4413 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4414 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4415 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4416 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4417 {
4418 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4419 }
4420 FPrintF( stdout, ">\n" );
4421 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4422 FPrintF( stdout, "---\n" );
4423
4424 // Call getaddrinfo().
4425
4426 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4427 gettimeofday( &now, NULL );
4428 if( err )
4429 {
4430 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4431 }
4432 else
4433 {
4434 int addrCount = 0;
4435
4436 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4437
4438 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4439 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4440 {
4441 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4442 }
4443 }
4444 FPrintF( stdout, "---\n" );
4445 FPrintF( stdout, "End time: %{du:time}\n", &now );
4446
4447 exit:
4448 if( addrInfoList ) freeaddrinfo( addrInfoList );
4449 if( err ) exit( 1 );
4450 }
4451
4452 //===========================================================================================================================
4453 // ReverseLookupCmd
4454 //===========================================================================================================================
4455
4456 #define kIP6ARPADomainStr "ip6.arpa."
4457
4458 static void ReverseLookupCmd( void )
4459 {
4460 OSStatus err;
4461 QueryRecordContext * context = NULL;
4462 DNSServiceRef sdRef;
4463 dispatch_source_t signalSource = NULL;
4464 uint32_t ipv4Addr;
4465 uint8_t ipv6Addr[ 16 ];
4466 char recordName[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr ) ];
4467 int useMainConnection;
4468 const char * endPtr;
4469
4470 // Set up SIGINT handler.
4471
4472 signal( SIGINT, SIG_IGN );
4473 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4474 require_noerr( err, exit );
4475 dispatch_resume( signalSource );
4476
4477 // Create context.
4478
4479 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
4480 require_action( context, exit, err = kNoMemoryErr );
4481
4482 // Check command parameters.
4483
4484 if( gReverseLookup_TimeLimitSecs < 0 )
4485 {
4486 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
4487 err = kParamErr;
4488 goto exit;
4489 }
4490
4491 // Create main connection.
4492
4493 if( gConnectionOpt )
4494 {
4495 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4496 require_noerr_quiet( err, exit );
4497 useMainConnection = true;
4498 }
4499 else
4500 {
4501 useMainConnection = false;
4502 }
4503
4504 // Get flags.
4505
4506 context->flags = GetDNSSDFlagsFromOpts();
4507 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4508
4509 // Get interface index.
4510
4511 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4512 require_noerr_quiet( err, exit );
4513
4514 // Create reverse lookup record name.
4515
4516 err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
4517 &ipv4Addr, NULL, NULL, NULL, &endPtr );
4518 if( err || ( *endPtr != '\0' ) )
4519 {
4520 char * dst;
4521 int i;
4522
4523 err = _StringToIPv6Address( gReverseLookup_IPAddr,
4524 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
4525 ipv6Addr, NULL, NULL, NULL, &endPtr );
4526 if( err || ( *endPtr != '\0' ) )
4527 {
4528 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
4529 err = kParamErr;
4530 goto exit;
4531 }
4532 dst = recordName;
4533 for( i = 15; i >= 0; --i )
4534 {
4535 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
4536 *dst++ = '.';
4537 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
4538 *dst++ = '.';
4539 }
4540 strcpy_literal( dst, kIP6ARPADomainStr );
4541 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
4542 }
4543 else
4544 {
4545 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
4546 ipv4Addr & 0xFF,
4547 ( ipv4Addr >> 8 ) & 0xFF,
4548 ( ipv4Addr >> 16 ) & 0xFF,
4549 ( ipv4Addr >> 24 ) & 0xFF );
4550 }
4551
4552 // Set remaining parameters.
4553
4554 context->recordName = recordName;
4555 context->recordType = kDNSServiceType_PTR;
4556 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
4557 context->oneShotMode = gReverseLookup_OneShot ? true : false;
4558
4559 // Print prologue.
4560
4561 QueryRecordPrintPrologue( context );
4562
4563 // Start operation.
4564
4565 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4566 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
4567 kDNSServiceClass_IN, QueryRecordCallback, context );
4568 require_noerr( err, exit );
4569
4570 context->opRef = sdRef;
4571 if( !useMainConnection )
4572 {
4573 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4574 require_noerr( err, exit );
4575 }
4576
4577 // Set time limit.
4578
4579 if( context->timeLimitSecs > 0 )
4580 {
4581 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4582 kExitReason_TimeLimit, Exit );
4583 }
4584 dispatch_main();
4585
4586 exit:
4587 dispatch_source_forget( &signalSource );
4588 if( context ) QueryRecordContextFree( context );
4589 if( err ) exit( 1 );
4590 }
4591
4592 //===========================================================================================================================
4593 // PortMappingCmd
4594 //===========================================================================================================================
4595
4596 typedef struct
4597 {
4598 DNSServiceRef mainRef; // Main sdRef for shared connection.
4599 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
4600 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
4601 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4602 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4603 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
4604 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4605 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
4606 Boolean printedHeader; // True if results header was printed.
4607
4608 } PortMappingContext;
4609
4610 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
4611 static void PortMappingContextFree( PortMappingContext *inContext );
4612 static void DNSSD_API
4613 PortMappingCallback(
4614 DNSServiceRef inSDRef,
4615 DNSServiceFlags inFlags,
4616 uint32_t inInterfaceIndex,
4617 DNSServiceErrorType inError,
4618 uint32_t inExternalIPv4Address,
4619 DNSServiceProtocol inProtocol,
4620 uint16_t inInternalPort,
4621 uint16_t inExternalPort,
4622 uint32_t inTTL,
4623 void * inContext );
4624
4625 static void PortMappingCmd( void )
4626 {
4627 OSStatus err;
4628 PortMappingContext * context = NULL;
4629 DNSServiceRef sdRef;
4630 dispatch_source_t signalSource = NULL;
4631 int useMainConnection;
4632
4633 // Set up SIGINT handler.
4634
4635 signal( SIGINT, SIG_IGN );
4636 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4637 require_noerr( err, exit );
4638 dispatch_resume( signalSource );
4639
4640 // Create context.
4641
4642 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
4643 require_action( context, exit, err = kNoMemoryErr );
4644
4645 // Check command parameters.
4646
4647 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
4648 {
4649 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
4650 err = kParamErr;
4651 goto exit;
4652 }
4653
4654 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
4655 {
4656 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
4657 err = kParamErr;
4658 goto exit;
4659 }
4660
4661 // Create main connection.
4662
4663 if( gConnectionOpt )
4664 {
4665 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4666 require_noerr_quiet( err, exit );
4667 useMainConnection = true;
4668 }
4669 else
4670 {
4671 useMainConnection = false;
4672 }
4673
4674 // Get flags.
4675
4676 context->flags = GetDNSSDFlagsFromOpts();
4677 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4678
4679 // Get interface index.
4680
4681 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4682 require_noerr_quiet( err, exit );
4683
4684 // Set remaining parameters.
4685
4686 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
4687 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
4688 context->ttl = (uint32_t) gPortMapping_TTL;
4689 context->internalPort = (uint16_t) gPortMapping_InternalPort;
4690 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
4691
4692 // Print prologue.
4693
4694 PortMappingPrintPrologue( context );
4695
4696 // Start operation.
4697
4698 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4699 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
4700 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
4701 require_noerr( err, exit );
4702
4703 context->opRef = sdRef;
4704 if( !useMainConnection )
4705 {
4706 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4707 require_noerr( err, exit );
4708 }
4709
4710 dispatch_main();
4711
4712 exit:
4713 dispatch_source_forget( &signalSource );
4714 if( context ) PortMappingContextFree( context );
4715 if( err ) exit( 1 );
4716 }
4717
4718 //===========================================================================================================================
4719 // PortMappingPrintPrologue
4720 //===========================================================================================================================
4721
4722 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
4723 {
4724 char ifName[ kInterfaceNameBufLen ];
4725
4726 InterfaceIndexToName( inContext->ifIndex, ifName );
4727
4728 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4729 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4730 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
4731 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
4732 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
4733 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
4734 " (system will use a default value.)" );
4735 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4736 FPrintF( stdout, "---\n" );
4737
4738 }
4739
4740 //===========================================================================================================================
4741 // PortMappingContextFree
4742 //===========================================================================================================================
4743
4744 static void PortMappingContextFree( PortMappingContext *inContext )
4745 {
4746 DNSServiceForget( &inContext->opRef );
4747 DNSServiceForget( &inContext->mainRef );
4748 free( inContext );
4749 }
4750
4751 //===========================================================================================================================
4752 // PortMappingCallback
4753 //===========================================================================================================================
4754
4755 static void DNSSD_API
4756 PortMappingCallback(
4757 DNSServiceRef inSDRef,
4758 DNSServiceFlags inFlags,
4759 uint32_t inInterfaceIndex,
4760 DNSServiceErrorType inError,
4761 uint32_t inExternalIPv4Address,
4762 DNSServiceProtocol inProtocol,
4763 uint16_t inInternalPort,
4764 uint16_t inExternalPort,
4765 uint32_t inTTL,
4766 void * inContext )
4767 {
4768 PortMappingContext * const context = (PortMappingContext *) inContext;
4769 struct timeval now;
4770 char errorStr[ 128 ];
4771
4772 Unused( inSDRef );
4773 Unused( inFlags );
4774
4775 gettimeofday( &now, NULL );
4776
4777 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
4778 if( !context->printedHeader )
4779 {
4780 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4781 context->printedHeader = true;
4782 }
4783 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4784 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
4785 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
4786 }
4787
4788 //===========================================================================================================================
4789 // BrowseAllCmd
4790 //===========================================================================================================================
4791
4792 typedef struct BrowseAllConnection BrowseAllConnection;
4793
4794 typedef struct
4795 {
4796 ServiceBrowserRef browser; // Service browser.
4797 ServiceBrowserResults * results; // Results from the service browser.
4798 BrowseAllConnection * connectionList; // List of connections.
4799 dispatch_source_t connectionTimer; // Timer for connection timeout.
4800 int connectionPendingCount; // Number of pending connections.
4801 int connectionTimeoutSecs; // Timeout value for connections in seconds.
4802
4803 } BrowseAllContext;
4804
4805 struct BrowseAllConnection
4806 {
4807 BrowseAllConnection * next; // Next connection object in list.
4808 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
4809 uint16_t port; // TCP port to connect to.
4810 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
4811 OSStatus status; // Status of connection. NoErr means connection succeeded.
4812 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
4813 int32_t refCount; // This object's reference count.
4814 BrowseAllContext * context; // Back pointer to parent context.
4815 };
4816
4817 static void _BrowseAllContextFree( BrowseAllContext *inContext );
4818 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
4819 static OSStatus
4820 _BrowseAllConnectionCreate(
4821 const struct sockaddr * inSockAddr,
4822 uint16_t inPort,
4823 BrowseAllContext * inContext,
4824 BrowseAllConnection ** outConnection );
4825 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
4826 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
4827 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
4828 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
4829 static void _BrowseAllExit( void *inContext );
4830
4831 static Boolean _IsServiceTypeTCP( const char *inServiceType );
4832
4833 static void BrowseAllCmd( void )
4834 {
4835 OSStatus err;
4836 BrowseAllContext * context = NULL;
4837 size_t i;
4838 uint32_t ifIndex;
4839 char ifName[ kInterfaceNameBufLen ];
4840
4841 // Check parameters.
4842
4843 if( gBrowseAll_BrowseTimeSecs <= 0 )
4844 {
4845 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
4846 err = kParamErr;
4847 goto exit;
4848 }
4849
4850 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
4851 require_action( context, exit, err = kNoMemoryErr );
4852
4853 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
4854 #if( TARGET_OS_POSIX )
4855 // Increase the open file descriptor limit for connection sockets.
4856
4857 if( context->connectionTimeoutSecs > 0 )
4858 {
4859 struct rlimit fdLimits;
4860
4861 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
4862 err = map_global_noerr_errno( err );
4863 require_noerr( err, exit );
4864
4865 if( fdLimits.rlim_cur < 4096 )
4866 {
4867 fdLimits.rlim_cur = 4096;
4868 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
4869 err = map_global_noerr_errno( err );
4870 require_noerr( err, exit );
4871 }
4872 }
4873 #endif
4874
4875 // Get interface index.
4876
4877 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4878 require_noerr_quiet( err, exit );
4879
4880 // Print prologue.
4881
4882 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4883 FPrintF( stdout, "Service types: ");
4884 if( gBrowseAll_ServiceTypesCount > 0 )
4885 {
4886 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
4887 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
4888 {
4889 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
4890 }
4891 FPrintF( stdout, "\n" );
4892 }
4893 else
4894 {
4895 FPrintF( stdout, "all services\n" );
4896 }
4897 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
4898 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
4899 FPrintF( stdout, "Connect timeout: %d second%?c\n",
4900 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
4901 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
4902 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4903 FPrintF( stdout, "---\n" );
4904
4905 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
4906 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
4907 require_noerr( err, exit );
4908
4909 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
4910 {
4911 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
4912 require_noerr( err, exit );
4913 }
4914 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
4915 ServiceBrowserStart( context->browser );
4916 dispatch_main();
4917
4918 exit:
4919 if( context ) _BrowseAllContextFree( context );
4920 if( err ) exit( 1 );
4921 }
4922
4923 //===========================================================================================================================
4924 // _BrowseAllContextFree
4925 //===========================================================================================================================
4926
4927 static void _BrowseAllContextFree( BrowseAllContext *inContext )
4928 {
4929 check( !inContext->browser );
4930 check( !inContext->connectionTimer );
4931 check( !inContext->connectionList );
4932 ForgetServiceBrowserResults( &inContext->results );
4933 free( inContext );
4934 }
4935
4936 //===========================================================================================================================
4937 // _BrowseAllServiceBrowserCallback
4938 //===========================================================================================================================
4939
4940 #define kDiscardProtocolPort 9
4941
4942 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
4943 {
4944 OSStatus err;
4945 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4946 SBRDomain * domain;
4947 SBRServiceType * type;
4948 SBRServiceInstance * instance;
4949 SBRIPAddress * ipaddr;
4950
4951 Unused( inError );
4952
4953 require_action( inResults, exit, err = kUnexpectedErr );
4954
4955 check( !context->results );
4956 context->results = inResults;
4957 ServiceBrowserResultsRetain( context->results );
4958
4959 check( context->connectionPendingCount == 0 );
4960 if( context->connectionTimeoutSecs > 0 )
4961 {
4962 BrowseAllConnection * connection;
4963 BrowseAllConnection ** connectionPtr = &context->connectionList;
4964 char destination[ kSockAddrStringMaxSize ];
4965
4966 for( domain = context->results->domainList; domain; domain = domain->next )
4967 {
4968 for( type = domain->typeList; type; type = type->next )
4969 {
4970 if( !_IsServiceTypeTCP( type->name ) ) continue;
4971 for( instance = type->instanceList; instance; instance = instance->next )
4972 {
4973 if( instance->port == kDiscardProtocolPort ) continue;
4974 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
4975 {
4976 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
4977 require_noerr( err, exit );
4978
4979 *connectionPtr = connection;
4980 connectionPtr = &connection->next;
4981
4982 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
4983 check_noerr( err );
4984 if( !err )
4985 {
4986 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
4987 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
4988 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
4989 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
4990 dispatch_get_main_queue() );
4991 check_noerr( err );
4992 }
4993 if( !err )
4994 {
4995 _BrowseAllConnectionRetain( connection );
4996 connection->status = kInProgressErr;
4997 ++context->connectionPendingCount;
4998 }
4999 else
5000 {
5001 connection->status = err;
5002 }
5003 }
5004 }
5005 }
5006 }
5007 }
5008
5009 if( context->connectionPendingCount > 0 )
5010 {
5011 check( !context->connectionTimer );
5012 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5013 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5014 require_noerr( err, exit );
5015 dispatch_resume( context->connectionTimer );
5016 }
5017 else
5018 {
5019 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5020 }
5021 err = kNoErr;
5022
5023 exit:
5024 ForgetCF( &context->browser );
5025 if( err ) exit( 1 );
5026 }
5027
5028 //===========================================================================================================================
5029 // _BrowseAllConnectionCreate
5030 //===========================================================================================================================
5031
5032 static OSStatus
5033 _BrowseAllConnectionCreate(
5034 const struct sockaddr * inSockAddr,
5035 uint16_t inPort,
5036 BrowseAllContext * inContext,
5037 BrowseAllConnection ** outConnection )
5038 {
5039 OSStatus err;
5040 BrowseAllConnection * obj;
5041
5042 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5043 require_action( obj, exit, err = kNoMemoryErr );
5044
5045 obj->refCount = 1;
5046 SockAddrCopy( inSockAddr, &obj->sip );
5047 obj->port = inPort;
5048 obj->context = inContext;
5049
5050 *outConnection = obj;
5051 err = kNoErr;
5052
5053 exit:
5054 return( err );
5055 }
5056
5057 //===========================================================================================================================
5058 // _BrowseAllConnectionRetain
5059 //===========================================================================================================================
5060
5061 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5062 {
5063 ++inConnection->refCount;
5064 }
5065
5066 //===========================================================================================================================
5067 // _BrowseAllConnectionRelease
5068 //===========================================================================================================================
5069
5070 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5071 {
5072 if( --inConnection->refCount == 0 ) free( inConnection );
5073 }
5074
5075 //===========================================================================================================================
5076 // _BrowseAllConnectionProgress
5077 //===========================================================================================================================
5078
5079 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5080 {
5081 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5082
5083 if( inPhase == kAsyncConnectionPhase_Connected )
5084 {
5085 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5086
5087 connection->connectTimeSecs = info->connectSecs;
5088 }
5089 }
5090
5091 //===========================================================================================================================
5092 // _BrowseAllConnectionHandler
5093 //===========================================================================================================================
5094
5095 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5096 {
5097 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5098 BrowseAllContext * const context = connection->context;
5099
5100 connection->status = inError;
5101 ForgetSocket( &inSock );
5102 if( context )
5103 {
5104 check( context->connectionPendingCount > 0 );
5105 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5106 {
5107 dispatch_source_forget( &context->connectionTimer );
5108 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5109 }
5110 }
5111 _BrowseAllConnectionRelease( connection );
5112 }
5113
5114 //===========================================================================================================================
5115 // _BrowseAllExit
5116 //===========================================================================================================================
5117
5118 #define Indent( X ) ( (X) * 4 ), ""
5119
5120 static void _BrowseAllExit( void *inContext )
5121 {
5122 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5123 SBRDomain * domain;
5124 SBRServiceType * type;
5125 SBRServiceInstance * instance;
5126 SBRIPAddress * ipaddr;
5127 char textBuf[ 512 ];
5128 #if( TARGET_OS_POSIX )
5129 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5130 #endif
5131
5132 dispatch_source_forget( &context->connectionTimer );
5133
5134 for( domain = context->results->domainList; domain; domain = domain->next )
5135 {
5136 FPrintF( stdout, "%s\n\n", domain->name );
5137
5138 for( type = domain->typeList; type; type = type->next )
5139 {
5140 const char * description;
5141 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5142
5143 description = ServiceTypeDescription( type->name );
5144 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5145 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5146
5147 for( instance = type->instanceList; instance; instance = instance->next )
5148 {
5149 char * dst = textBuf;
5150 char * const lim = &textBuf[ countof( textBuf ) ];
5151 char ifname[ IF_NAMESIZE + 1 ];
5152
5153 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5154 if( instance->ifIndex == 0 )
5155 {
5156 SNPrintF_Add( &dst, lim, "the Internet" );
5157 }
5158 else if( if_indextoname( instance->ifIndex, ifname ) )
5159 {
5160 NetTransportType netType;
5161
5162 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5163 SNPrintF_Add( &dst, lim, "%s (%s)",
5164 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5165 ifname );
5166 }
5167 else
5168 {
5169 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5170 }
5171 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5172 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5173
5174 if( instance->hostname )
5175 {
5176 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5177 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5178 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5179 }
5180 else
5181 {
5182 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5183 }
5184
5185 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5186 {
5187 BrowseAllConnection * conn;
5188 BrowseAllConnection ** connPtr;
5189
5190 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5191 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5192
5193 conn = NULL;
5194 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5195 {
5196 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5197 {
5198 if( ( conn->port == instance->port ) &&
5199 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5200 }
5201 if( conn )
5202 {
5203 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5204 *connPtr = conn->next;
5205 conn->context = NULL;
5206 AsyncConnection_Forget( &conn->asyncCnx );
5207 }
5208 }
5209
5210 if( conn )
5211 {
5212 if( conn->status == kNoErr )
5213 {
5214 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5215 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5216 }
5217 else
5218 {
5219 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5220 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5221 }
5222 _BrowseAllConnectionRelease( conn );
5223 }
5224 else
5225 {
5226 FPrintF( stdout, " (no connection attempted)\n" );
5227 }
5228 }
5229
5230 FPrintF( stdout, "\n" );
5231 if( instance->txtLen == 0 ) continue;
5232
5233 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5234 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5235 if( instance->txtLen > 1 )
5236 {
5237 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5238 }
5239 else
5240 {
5241 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5242 }
5243 FPrintF( stdout, "\n" );
5244 }
5245 FPrintF( stdout, "\n" );
5246 }
5247 }
5248
5249 _BrowseAllContextFree( context );
5250 Exit( NULL );
5251 }
5252
5253 //===========================================================================================================================
5254 // _IsServiceTypeTCP
5255 //===========================================================================================================================
5256
5257 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5258 {
5259 OSStatus err;
5260 const uint8_t * secondLabel;
5261 uint8_t name[ kDomainNameLengthMax ];
5262
5263 err = DomainNameFromString( name, inServiceType, NULL );
5264 if( !err )
5265 {
5266 secondLabel = DomainNameGetNextLabel( name );
5267 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5268 }
5269 return( false );
5270 }
5271
5272 //===========================================================================================================================
5273 // GetNameInfoCmd
5274 //===========================================================================================================================
5275
5276 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5277 {
5278 CaseFlagStringify( NI_NUMERICSCOPE ),
5279 CaseFlagStringify( NI_DGRAM ),
5280 CaseFlagStringify( NI_NUMERICSERV ),
5281 CaseFlagStringify( NI_NAMEREQD ),
5282 CaseFlagStringify( NI_NUMERICHOST ),
5283 CaseFlagStringify( NI_NOFQDN ),
5284 { 0, NULL }
5285 };
5286
5287 static void GetNameInfoCmd( void )
5288 {
5289 OSStatus err;
5290 sockaddr_ip sip;
5291 size_t sockAddrLen;
5292 unsigned int flags;
5293 const FlagStringPair * pair;
5294 struct timeval now;
5295 char host[ NI_MAXHOST ];
5296 char serv[ NI_MAXSERV ];
5297
5298 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5299 check_noerr( err );
5300 if( err )
5301 {
5302 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5303 goto exit;
5304 }
5305
5306 flags = 0;
5307 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5308 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5309 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5310 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5311 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5312 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5313
5314 // Print prologue.
5315
5316 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5317 FPrintF( stdout, "Flags: 0x%X < ", flags );
5318 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5319 {
5320 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5321 }
5322 FPrintF( stdout, ">\n" );
5323 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5324 FPrintF( stdout, "---\n" );
5325
5326 // Call getnameinfo().
5327
5328 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5329 (int) flags );
5330 gettimeofday( &now, NULL );
5331 if( err )
5332 {
5333 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5334 }
5335 else
5336 {
5337 FPrintF( stdout, "host: %s\n", host );
5338 FPrintF( stdout, "serv: %s\n", serv );
5339 }
5340 FPrintF( stdout, "---\n" );
5341 FPrintF( stdout, "End time: %{du:time}\n", &now );
5342
5343 exit:
5344 gExitCode = err ? 1 : 0;
5345 }
5346
5347 //===========================================================================================================================
5348 // GetAddrInfoStressCmd
5349 //===========================================================================================================================
5350
5351 typedef struct
5352 {
5353 DNSServiceRef mainRef;
5354 DNSServiceRef sdRef;
5355 DNSServiceFlags flags;
5356 unsigned int interfaceIndex;
5357 unsigned int connectionNumber;
5358 unsigned int requestCount;
5359 unsigned int requestCountMax;
5360 unsigned int requestCountLimit;
5361 unsigned int durationMinMs;
5362 unsigned int durationMaxMs;
5363
5364 } GAIStressContext;
5365
5366 static void GetAddrInfoStressEvent( void *inContext );
5367 static void DNSSD_API
5368 GetAddrInfoStressCallback(
5369 DNSServiceRef inSDRef,
5370 DNSServiceFlags inFlags,
5371 uint32_t inInterfaceIndex,
5372 DNSServiceErrorType inError,
5373 const char * inHostname,
5374 const struct sockaddr * inSockAddr,
5375 uint32_t inTTL,
5376 void * inContext );
5377
5378 static void GetAddrInfoStressCmd( void )
5379 {
5380 OSStatus err;
5381 GAIStressContext * context = NULL;
5382 int i;
5383 DNSServiceFlags flags;
5384 uint32_t ifIndex;
5385 char ifName[ kInterfaceNameBufLen ];
5386
5387 if( gGAIStress_TestDurationSecs < 0 )
5388 {
5389 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5390 err = kParamErr;
5391 goto exit;
5392 }
5393 if( gGAIStress_ConnectionCount <= 0 )
5394 {
5395 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5396 err = kParamErr;
5397 goto exit;
5398 }
5399 if( gGAIStress_DurationMinMs <= 0 )
5400 {
5401 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5402 err = kParamErr;
5403 goto exit;
5404 }
5405 if( gGAIStress_DurationMaxMs <= 0 )
5406 {
5407 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5408 err = kParamErr;
5409 goto exit;
5410 }
5411 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5412 {
5413 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5414 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5415 err = kParamErr;
5416 goto exit;
5417 }
5418 if( gGAIStress_RequestCountMax <= 0 )
5419 {
5420 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5421 err = kParamErr;
5422 goto exit;
5423 }
5424
5425 // Set flags.
5426
5427 flags = GetDNSSDFlagsFromOpts();
5428
5429 // Set interface index.
5430
5431 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5432 require_noerr_quiet( err, exit );
5433
5434 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5435 {
5436 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5437 require_action( context, exit, err = kNoMemoryErr );
5438
5439 context->flags = flags;
5440 context->interfaceIndex = ifIndex;
5441 context->connectionNumber = (unsigned int)( i + 1 );
5442 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5443 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5444 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5445
5446 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5447 context = NULL;
5448 }
5449
5450 if( gGAIStress_TestDurationSecs > 0 )
5451 {
5452 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5453 }
5454
5455 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5456 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5457 FPrintF( stdout, "Test duration: " );
5458 if( gGAIStress_TestDurationSecs == 0 )
5459 {
5460 FPrintF( stdout, "∞\n" );
5461 }
5462 else
5463 {
5464 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5465 }
5466 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5467 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5468 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5469 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5470 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5471 FPrintF( stdout, "---\n" );
5472
5473 dispatch_main();
5474
5475 exit:
5476 FreeNullSafe( context );
5477 if( err ) exit( 1 );
5478 }
5479
5480 //===========================================================================================================================
5481 // GetAddrInfoStressEvent
5482 //===========================================================================================================================
5483
5484 #define kStressRandStrLen 5
5485
5486 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5487
5488 static void GetAddrInfoStressEvent( void *inContext )
5489 {
5490 GAIStressContext * const context = (GAIStressContext *) inContext;
5491 OSStatus err;
5492 DNSServiceRef sdRef;
5493 unsigned int nextMs;
5494 char randomStr[ kStressRandStrLen + 1 ];
5495 char hostname[ kStressRandStrLen + 4 + 1 ];
5496 Boolean isConnectionNew = false;
5497 static Boolean printedHeader = false;
5498
5499 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5500 {
5501 DNSServiceForget( &context->mainRef );
5502 context->sdRef = NULL;
5503 context->requestCount = 0;
5504 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5505
5506 err = DNSServiceCreateConnection( &context->mainRef );
5507 require_noerr( err, exit );
5508
5509 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5510 require_noerr( err, exit );
5511
5512 isConnectionNew = true;
5513 }
5514
5515 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5516 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5517
5518 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5519
5520 if( !printedHeader )
5521 {
5522 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5523 printedHeader = true;
5524 }
5525 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5526 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5527
5528 DNSServiceForget( &context->sdRef );
5529 sdRef = context->mainRef;
5530 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5531 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5532 require_noerr( err, exit );
5533 context->sdRef = sdRef;
5534
5535 context->requestCount++;
5536
5537 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5538
5539 exit:
5540 if( err ) exit( 1 );
5541 }
5542
5543 //===========================================================================================================================
5544 // GetAddrInfoStressCallback
5545 //===========================================================================================================================
5546
5547 static void DNSSD_API
5548 GetAddrInfoStressCallback(
5549 DNSServiceRef inSDRef,
5550 DNSServiceFlags inFlags,
5551 uint32_t inInterfaceIndex,
5552 DNSServiceErrorType inError,
5553 const char * inHostname,
5554 const struct sockaddr * inSockAddr,
5555 uint32_t inTTL,
5556 void * inContext )
5557 {
5558 Unused( inSDRef );
5559 Unused( inFlags );
5560 Unused( inInterfaceIndex );
5561 Unused( inError );
5562 Unused( inHostname );
5563 Unused( inSockAddr );
5564 Unused( inTTL );
5565 Unused( inContext );
5566 }
5567
5568 //===========================================================================================================================
5569 // DNSQueryCmd
5570 //===========================================================================================================================
5571
5572 typedef struct
5573 {
5574 sockaddr_ip serverAddr;
5575 uint64_t sendTicks;
5576 uint8_t * msgPtr;
5577 size_t msgLen;
5578 size_t msgOffset;
5579 const char * name;
5580 dispatch_source_t readSource;
5581 SocketRef sock;
5582 int timeLimitSecs;
5583 uint16_t queryID;
5584 uint16_t type;
5585 Boolean haveTCPLen;
5586 Boolean useTCP;
5587 Boolean printRawRData; // True if RDATA results are not to be formatted.
5588 uint8_t msgBuf[ 512 ];
5589
5590 } DNSQueryContext;
5591
5592 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5593 static void DNSQueryReadHandler( void *inContext );
5594 static void DNSQueryCancelHandler( void *inContext );
5595
5596 static void DNSQueryCmd( void )
5597 {
5598 OSStatus err;
5599 DNSQueryContext * context = NULL;
5600 uint8_t * msgPtr;
5601 size_t msgLen, sendLen;
5602
5603 // Check command parameters.
5604
5605 if( gDNSQuery_TimeLimitSecs < -1 )
5606 {
5607 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5608 err = kParamErr;
5609 goto exit;
5610 }
5611 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5612 {
5613 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5614 err = kParamErr;
5615 goto exit;
5616 }
5617
5618 // Create context.
5619
5620 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5621 require_action( context, exit, err = kNoMemoryErr );
5622
5623 context->name = gDNSQuery_Name;
5624 context->sock = kInvalidSocketRef;
5625 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5626 context->queryID = (uint16_t) Random32();
5627 context->useTCP = gDNSQuery_UseTCP ? true : false;
5628 context->printRawRData = gDNSQuery_RawRData ? true : false;
5629
5630 #if( TARGET_OS_DARWIN )
5631 if( gDNSQuery_Server )
5632 #endif
5633 {
5634 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5635 require_noerr( err, exit );
5636 }
5637 #if( TARGET_OS_DARWIN )
5638 else
5639 {
5640 err = GetDefaultDNSServer( &context->serverAddr );
5641 require_noerr( err, exit );
5642 }
5643 #endif
5644 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5645
5646 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5647 require_noerr( err, exit );
5648
5649 // Write query message.
5650
5651 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5652
5653 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5654 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5655 kDNSServiceClass_IN, &msgLen );
5656 require_noerr( err, exit );
5657 check( msgLen <= UINT16_MAX );
5658
5659 if( context->useTCP )
5660 {
5661 WriteBig16( context->msgBuf, msgLen );
5662 sendLen = 2 + msgLen;
5663 }
5664 else
5665 {
5666 sendLen = msgLen;
5667 }
5668
5669 DNSQueryPrintPrologue( context );
5670
5671 if( gDNSQuery_Verbose )
5672 {
5673 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5674 FPrintF( stdout, "---\n" );
5675 }
5676
5677 if( context->useTCP )
5678 {
5679 // Create TCP socket.
5680
5681 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5682 err = map_socket_creation_errno( context->sock );
5683 require_noerr( err, exit );
5684
5685 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5686 require_noerr( err, exit );
5687 }
5688 else
5689 {
5690 // Create UDP socket.
5691
5692 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5693 require_noerr( err, exit );
5694 }
5695
5696 context->sendTicks = UpTicks();
5697 err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5698 require_noerr( err, exit );
5699
5700 if( context->timeLimitSecs == 0 ) goto exit;
5701
5702 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5703 &context->readSource );
5704 require_noerr( err, exit );
5705 dispatch_resume( context->readSource );
5706
5707 if( context->timeLimitSecs > 0 )
5708 {
5709 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5710 Exit );
5711 }
5712 dispatch_main();
5713
5714 exit:
5715 if( context )
5716 {
5717 dispatch_source_forget( &context->readSource );
5718 ForgetSocket( &context->sock );
5719 free( context );
5720 }
5721 if( err ) exit( 1 );
5722 }
5723
5724 //===========================================================================================================================
5725 // DNSQueryPrintPrologue
5726 //===========================================================================================================================
5727
5728 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5729 {
5730 const int timeLimitSecs = inContext->timeLimitSecs;
5731
5732 FPrintF( stdout, "Name: %s\n", inContext->name );
5733 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5734 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5735 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5736 FPrintF( stdout, "Time limit: " );
5737 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5738 else FPrintF( stdout, "∞\n" );
5739 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5740 FPrintF( stdout, "---\n" );
5741 }
5742
5743 //===========================================================================================================================
5744 // DNSQueryReadHandler
5745 //===========================================================================================================================
5746
5747 static void DNSQueryReadHandler( void *inContext )
5748 {
5749 OSStatus err;
5750 struct timeval now;
5751 const uint64_t nowTicks = UpTicks();
5752 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5753
5754 gettimeofday( &now, NULL );
5755
5756 if( context->useTCP )
5757 {
5758 if( !context->haveTCPLen )
5759 {
5760 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5761 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5762 require_noerr( err, exit );
5763
5764 context->msgOffset = 0;
5765 context->msgLen = ReadBig16( context->msgBuf );
5766 context->haveTCPLen = true;
5767 if( context->msgLen <= sizeof( context->msgBuf ) )
5768 {
5769 context->msgPtr = context->msgBuf;
5770 }
5771 else
5772 {
5773 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5774 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5775 }
5776 }
5777
5778 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5779 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5780 require_noerr( err, exit );
5781 context->msgOffset = 0;
5782 context->haveTCPLen = false;
5783 }
5784 else
5785 {
5786 sockaddr_ip fromAddr;
5787
5788 context->msgPtr = context->msgBuf;
5789 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5790 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5791 require_noerr( err, exit );
5792
5793 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5794 }
5795
5796 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
5797 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5798 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5799 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5800 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
5801
5802 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5803 {
5804 Exit( kExitReason_ReceivedResponse );
5805 }
5806
5807 exit:
5808 if( err ) dispatch_source_forget( &context->readSource );
5809 }
5810
5811 //===========================================================================================================================
5812 // DNSQueryCancelHandler
5813 //===========================================================================================================================
5814
5815 static void DNSQueryCancelHandler( void *inContext )
5816 {
5817 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5818
5819 check( !context->readSource );
5820 ForgetSocket( &context->sock );
5821 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5822 free( context );
5823 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5824 }
5825
5826 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5827 //===========================================================================================================================
5828 // DNSCryptCmd
5829 //===========================================================================================================================
5830
5831 #define kDNSCryptPort 443
5832
5833 #define kDNSCryptMinPadLength 8
5834 #define kDNSCryptMaxPadLength 256
5835 #define kDNSCryptBlockSize 64
5836 #define kDNSCryptCertMinimumLength 124
5837 #define kDNSCryptClientMagicLength 8
5838 #define kDNSCryptResolverMagicLength 8
5839 #define kDNSCryptHalfNonceLength 12
5840 #define kDNSCryptCertMagicLength 4
5841
5842 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5843
5844 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5845 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5846 {
5847 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5848 };
5849
5850 typedef struct
5851 {
5852 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5853 uint8_t esVersion[ 2 ];
5854 uint8_t minorVersion[ 2 ];
5855 uint8_t signature[ crypto_sign_BYTES ];
5856 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5857 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5858 uint8_t serial[ 4 ];
5859 uint8_t startTime[ 4 ];
5860 uint8_t endTime[ 4 ];
5861 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5862
5863 } DNSCryptCert;
5864
5865 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5866
5867 typedef struct
5868 {
5869 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5870 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5871 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5872 uint8_t poly1305MAC[ 16 ];
5873
5874 } DNSCryptQueryHeader;
5875
5876 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5877 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5878 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5879 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5880
5881 typedef struct
5882 {
5883 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5884 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5885 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5886 uint8_t poly1305MAC[ 16 ];
5887
5888 } DNSCryptResponseHeader;
5889
5890 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5891 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5892 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5893 sizeof( DNSCryptResponseHeader ) );
5894
5895 typedef struct
5896 {
5897 sockaddr_ip serverAddr;
5898 uint64_t sendTicks;
5899 const char * providerName;
5900 const char * qname;
5901 const uint8_t * certPtr;
5902 size_t certLen;
5903 dispatch_source_t readSource;
5904 size_t msgLen;
5905 int timeLimitSecs;
5906 uint16_t queryID;
5907 uint16_t qtype;
5908 Boolean printRawRData;
5909 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5910 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5911 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5912 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5913 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5914 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5915 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5916 uint8_t msgBuf[ 512 ];
5917
5918 } DNSCryptContext;
5919
5920 static void DNSCryptReceiveCertHandler( void *inContext );
5921 static void DNSCryptReceiveResponseHandler( void *inContext );
5922 static void DNSCryptProceed( void *inContext );
5923 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5924 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5925 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5926 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5927
5928 static void DNSCryptCmd( void )
5929 {
5930 OSStatus err;
5931 DNSCryptContext * context = NULL;
5932 size_t writtenBytes;
5933 size_t totalBytes;
5934 SocketContext * sockCtx;
5935 SocketRef sock = kInvalidSocketRef;
5936 const char * ptr;
5937
5938 // Check command parameters.
5939
5940 if( gDNSCrypt_TimeLimitSecs < -1 )
5941 {
5942 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5943 err = kParamErr;
5944 goto exit;
5945 }
5946
5947 // Create context.
5948
5949 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5950 require_action( context, exit, err = kNoMemoryErr );
5951
5952 context->providerName = gDNSCrypt_ProviderName;
5953 context->qname = gDNSCrypt_Name;
5954 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5955 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5956
5957 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5958 require_noerr( err, exit );
5959
5960 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5961 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5962 if( err || ( *ptr != '\0' ) )
5963 {
5964 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5965 goto exit;
5966 }
5967 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5968 {
5969 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5970 totalBytes, sizeof( context->serverPublicSignKey ) );
5971 err = kSizeErr;
5972 goto exit;
5973 }
5974 check( writtenBytes == totalBytes );
5975
5976 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5977 require_noerr( err, exit );
5978 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
5979
5980 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
5981 require_noerr( err, exit );
5982
5983 // Write query message.
5984
5985 context->queryID = (uint16_t) Random32();
5986 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
5987 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
5988 require_noerr( err, exit );
5989
5990 // Create UDP socket.
5991
5992 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
5993 require_noerr( err, exit );
5994
5995 // Send DNS query.
5996
5997 context->sendTicks = UpTicks();
5998 err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
5999 require_noerr( err, exit );
6000
6001 err = SocketContextCreate( sock, context, &sockCtx );
6002 require_noerr( err, exit );
6003 sock = kInvalidSocketRef;
6004
6005 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6006 &context->readSource );
6007 if( err ) ForgetSocketContext( &sockCtx );
6008 require_noerr( err, exit );
6009
6010 dispatch_resume( context->readSource );
6011
6012 if( context->timeLimitSecs > 0 )
6013 {
6014 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6015 Exit );
6016 }
6017 dispatch_main();
6018
6019 exit:
6020 if( context ) free( context );
6021 ForgetSocket( &sock );
6022 if( err ) exit( 1 );
6023 }
6024
6025 //===========================================================================================================================
6026 // DNSCryptReceiveCertHandler
6027 //===========================================================================================================================
6028
6029 static void DNSCryptReceiveCertHandler( void *inContext )
6030 {
6031 OSStatus err;
6032 struct timeval now;
6033 const uint64_t nowTicks = UpTicks();
6034 SocketContext * const sockCtx = (SocketContext *) inContext;
6035 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6036 const DNSHeader * hdr;
6037 sockaddr_ip fromAddr;
6038 const uint8_t * ptr;
6039 const uint8_t * txtPtr;
6040 size_t txtLen;
6041 unsigned int answerCount, i;
6042 uint8_t targetName[ kDomainNameLengthMax ];
6043
6044 gettimeofday( &now, NULL );
6045
6046 dispatch_source_forget( &context->readSource );
6047
6048 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6049 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6050 require_noerr( err, exit );
6051 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6052
6053 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6054 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6055 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6056 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6057 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6058
6059 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6060
6061 hdr = (DNSHeader *) context->msgBuf;
6062 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6063
6064 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6065 require_noerr( err, exit );
6066
6067 err = DomainNameFromString( targetName, context->providerName, NULL );
6068 require_noerr( err, exit );
6069
6070 answerCount = DNSHeaderGetAnswerCount( hdr );
6071 for( i = 0; i < answerCount; ++i )
6072 {
6073 uint16_t type;
6074 uint16_t class;
6075 uint8_t name[ kDomainNameLengthMax ];
6076
6077 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6078 &ptr );
6079 require_noerr( err, exit );
6080
6081 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6082 {
6083 break;
6084 }
6085 }
6086
6087 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6088 {
6089 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6090 err = kSizeErr;
6091 goto exit;
6092 }
6093 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6094 {
6095 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6096 err = kSizeErr;
6097 goto exit;
6098 }
6099
6100 context->certLen = txtPtr[ 0 ];
6101 context->certPtr = &txtPtr[ 1 ];
6102
6103 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6104
6105 exit:
6106 if( err ) Exit( NULL );
6107 }
6108
6109 //===========================================================================================================================
6110 // DNSCryptReceiveResponseHandler
6111 //===========================================================================================================================
6112
6113 static void DNSCryptReceiveResponseHandler( void *inContext )
6114 {
6115 OSStatus err;
6116 struct timeval now;
6117 const uint64_t nowTicks = UpTicks();
6118 SocketContext * const sockCtx = (SocketContext *) inContext;
6119 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6120 sockaddr_ip fromAddr;
6121 DNSCryptResponseHeader * hdr;
6122 const uint8_t * end;
6123 uint8_t * ciphertext;
6124 uint8_t * plaintext;
6125 const uint8_t * response;
6126 uint8_t nonce[ crypto_box_NONCEBYTES ];
6127
6128 gettimeofday( &now, NULL );
6129
6130 dispatch_source_forget( &context->readSource );
6131
6132 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6133 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6134 require_noerr( err, exit );
6135 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6136
6137 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6138 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6139 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6140 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6141
6142 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6143 {
6144 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6145 err = kSizeErr;
6146 goto exit;
6147 }
6148
6149 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6150
6151 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6152 {
6153 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6154 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6155 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6156 err = kValueErr;
6157 goto exit;
6158 }
6159
6160 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6161 {
6162 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6163 err = kValueErr;
6164 goto exit;
6165 }
6166
6167 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6168
6169 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6170 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6171
6172 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6173 check( plaintext == ciphertext );
6174
6175 end = context->msgBuf + context->msgLen;
6176
6177 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6178 require_noerr( err, exit );
6179
6180 response = plaintext + crypto_box_ZEROBYTES;
6181 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6182 Exit( kExitReason_ReceivedResponse );
6183
6184 exit:
6185 if( err ) Exit( NULL );
6186 }
6187
6188 //===========================================================================================================================
6189 // DNSCryptProceed
6190 //===========================================================================================================================
6191
6192 static void DNSCryptProceed( void *inContext )
6193 {
6194 OSStatus err;
6195 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6196
6197 err = DNSCryptProcessCert( context );
6198 require_noerr_quiet( err, exit );
6199
6200 err = DNSCryptBuildQuery( context );
6201 require_noerr_quiet( err, exit );
6202
6203 err = DNSCryptSendQuery( context );
6204 require_noerr_quiet( err, exit );
6205
6206 exit:
6207 if( err ) Exit( NULL );
6208 }
6209
6210 //===========================================================================================================================
6211 // DNSCryptProcessCert
6212 //===========================================================================================================================
6213
6214 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6215 {
6216 OSStatus err;
6217 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6218 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6219 struct timeval now;
6220 time_t startTimeSecs, endTimeSecs;
6221 size_t signedLen;
6222 uint8_t * tempBuf;
6223 unsigned long long tempLen;
6224
6225 DNSCryptPrintCertificate( cert, inContext->certLen );
6226
6227 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6228 {
6229 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6230 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6231 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6232 err = kValueErr;
6233 goto exit;
6234 }
6235
6236 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6237 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6238
6239 gettimeofday( &now, NULL );
6240 if( now.tv_sec < startTimeSecs )
6241 {
6242 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6243 err = kDateErr;
6244 goto exit;
6245 }
6246 if( now.tv_sec >= endTimeSecs )
6247 {
6248 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6249 err = kDateErr;
6250 goto exit;
6251 }
6252
6253 signedLen = (size_t)( certEnd - cert->signature );
6254 tempBuf = (uint8_t *) malloc( signedLen );
6255 require_action( tempBuf, exit, err = kNoMemoryErr );
6256 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6257 free( tempBuf );
6258 if( err )
6259 {
6260 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6261 err = kAuthenticationErr;
6262 goto exit;
6263 }
6264
6265 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6266 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6267
6268 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6269 require_noerr( err, exit );
6270
6271 inContext->certPtr = NULL;
6272 inContext->certLen = 0;
6273 inContext->msgLen = 0;
6274
6275 exit:
6276 return( err );
6277 }
6278
6279 //===========================================================================================================================
6280 // DNSCryptBuildQuery
6281 //===========================================================================================================================
6282
6283 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6284
6285 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6286 {
6287 OSStatus err;
6288 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6289 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6290 size_t queryLen;
6291 size_t paddedQueryLen;
6292 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6293 const uint8_t * padLimit;
6294 uint8_t nonce[ crypto_box_NONCEBYTES ];
6295
6296 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6297
6298 inContext->queryID = (uint16_t) Random32();
6299 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6300 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6301 require_noerr( err, exit );
6302
6303 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6304 if( padLimit > msgLimit ) padLimit = msgLimit;
6305
6306 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6307 require_noerr( err, exit );
6308
6309 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6310 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6311 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6312 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6313
6314 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6315 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6316 require_noerr( err, exit );
6317
6318 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6319 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6320 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6321
6322 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6323
6324 exit:
6325 return( err );
6326 }
6327
6328 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6329 {
6330 OSStatus err;
6331 size_t paddedLen;
6332
6333 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6334
6335 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6336 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6337 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6338 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6339
6340 inMsgPtr[ inMsgLen ] = 0x80;
6341 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6342
6343 if( outPaddedLen ) *outPaddedLen = paddedLen;
6344 err = kNoErr;
6345
6346 exit:
6347 return( err );
6348 }
6349
6350 //===========================================================================================================================
6351 // DNSCryptSendQuery
6352 //===========================================================================================================================
6353
6354 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6355 {
6356 OSStatus err;
6357 SocketContext * sockCtx;
6358 SocketRef sock = kInvalidSocketRef;
6359
6360 check( inContext->msgLen > 0 );
6361 check( !inContext->readSource );
6362
6363 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6364 require_noerr( err, exit );
6365
6366 inContext->sendTicks = UpTicks();
6367 err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6368 require_noerr( err, exit );
6369
6370 err = SocketContextCreate( sock, inContext, &sockCtx );
6371 require_noerr( err, exit );
6372 sock = kInvalidSocketRef;
6373
6374 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6375 &inContext->readSource );
6376 if( err ) ForgetSocketContext( &sockCtx );
6377 require_noerr( err, exit );
6378
6379 dispatch_resume( inContext->readSource );
6380
6381 exit:
6382 ForgetSocket( &sock );
6383 return( err );
6384 }
6385
6386 //===========================================================================================================================
6387 // DNSCryptPrintCertificate
6388 //===========================================================================================================================
6389
6390 #define kCertTimeStrBufLen 32
6391
6392 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6393
6394 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6395 {
6396 time_t startTime, endTime;
6397 int extLen;
6398 char timeBuf[ kCertTimeStrBufLen ];
6399
6400 check( inLen >= kDNSCryptCertMinimumLength );
6401
6402 startTime = (time_t) ReadBig32( inCert->startTime );
6403 endTime = (time_t) ReadBig32( inCert->endTime );
6404
6405 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6406 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6407 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6408 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6409 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6410 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6411 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6412 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6413 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6414 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6415 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6416
6417 if( inLen > kDNSCryptCertMinimumLength )
6418 {
6419 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6420 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6421 }
6422 FPrintF( stdout, "\n" );
6423 }
6424
6425 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6426 {
6427 struct tm * tm;
6428
6429 tm = localtime( &inTime );
6430 if( !tm )
6431 {
6432 dlogassert( "localtime() returned a NULL pointer.\n" );
6433 *inBuffer = '\0';
6434 }
6435 else
6436 {
6437 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6438 }
6439
6440 return( inBuffer );
6441 }
6442
6443 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6444
6445 //===========================================================================================================================
6446 // MDNSQueryCmd
6447 //===========================================================================================================================
6448
6449 typedef struct
6450 {
6451 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6452 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6453 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6454 int localPort; // The port number to which the sockets are bound.
6455 int receiveSecs; // After send, the amount of time to spend receiving.
6456 uint32_t ifIndex; // Index of the interface over which to send the query.
6457 uint16_t qtype; // The type (QTYPE) of the record being queried.
6458 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6459 Boolean allResponses; // True if all mDNS messages received should be printed.
6460 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6461 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6462 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6463 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6464 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6465 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
6466
6467 } MDNSQueryContext;
6468
6469 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6470 static void MDNSQueryReadHandler( void *inContext );
6471
6472 static void MDNSQueryCmd( void )
6473 {
6474 OSStatus err;
6475 MDNSQueryContext * context;
6476 SocketRef sockV4 = kInvalidSocketRef;
6477 SocketRef sockV6 = kInvalidSocketRef;
6478 ssize_t n;
6479 const char * ifname;
6480 size_t msgLen;
6481 unsigned int sendCount;
6482
6483 // Check command parameters.
6484
6485 if( gMDNSQuery_ReceiveSecs < -1 )
6486 {
6487 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6488 err = kParamErr;
6489 goto exit;
6490 }
6491
6492 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6493 require_action( context, exit, err = kNoMemoryErr );
6494
6495 context->qnameStr = gMDNSQuery_Name;
6496 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6497 context->isQU = gMDNSQuery_IsQU ? true : false;
6498 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6499 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6500 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6501 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6502
6503 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6504 require_noerr_quiet( err, exit );
6505
6506 ifname = if_indextoname( context->ifIndex, context->ifName );
6507 require_action( ifname, exit, err = kNameErr );
6508
6509 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6510 require_noerr( err, exit );
6511
6512 // Set up IPv4 socket.
6513
6514 if( context->useIPv4 )
6515 {
6516 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6517 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6518 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
6519 require_noerr( err, exit );
6520 }
6521
6522 // Set up IPv6 socket.
6523
6524 if( context->useIPv6 )
6525 {
6526 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6527 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6528 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
6529 require_noerr( err, exit );
6530 }
6531
6532 // Craft mDNS query message.
6533
6534 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6535 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6536 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6537 require_noerr( err, exit );
6538
6539 // Print prologue.
6540
6541 MDNSQueryPrintPrologue( context );
6542
6543 // Send mDNS query message.
6544
6545 sendCount = 0;
6546 if( IsValidSocket( sockV4 ) )
6547 {
6548 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
6549
6550 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
6551 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6552 if( err )
6553 {
6554 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6555 ForgetSocket( &sockV4 );
6556 }
6557 else
6558 {
6559 ++sendCount;
6560 }
6561 }
6562 if( IsValidSocket( sockV6 ) )
6563 {
6564 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
6565
6566 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
6567 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6568 if( err )
6569 {
6570 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6571 ForgetSocket( &sockV6 );
6572 }
6573 else
6574 {
6575 ++sendCount;
6576 }
6577 }
6578 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6579
6580 // If there's no wait period after the send, then exit.
6581
6582 if( context->receiveSecs == 0 ) goto exit;
6583
6584 // Create dispatch read sources for socket(s).
6585
6586 if( IsValidSocket( sockV4 ) )
6587 {
6588 SocketContext * sockCtx;
6589
6590 err = SocketContextCreate( sockV4, context, &sockCtx );
6591 require_noerr( err, exit );
6592 sockV4 = kInvalidSocketRef;
6593
6594 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6595 &context->readSourceV4 );
6596 if( err ) ForgetSocketContext( &sockCtx );
6597 require_noerr( err, exit );
6598
6599 dispatch_resume( context->readSourceV4 );
6600 }
6601
6602 if( IsValidSocket( sockV6 ) )
6603 {
6604 SocketContext * sockCtx;
6605
6606 err = SocketContextCreate( sockV6, context, &sockCtx );
6607 require_noerr( err, exit );
6608 sockV6 = kInvalidSocketRef;
6609
6610 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6611 &context->readSourceV6 );
6612 if( err ) ForgetSocketContext( &sockCtx );
6613 require_noerr( err, exit );
6614
6615 dispatch_resume( context->readSourceV6 );
6616 }
6617
6618 if( context->receiveSecs > 0 )
6619 {
6620 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6621 Exit );
6622 }
6623 dispatch_main();
6624
6625 exit:
6626 ForgetSocket( &sockV4 );
6627 ForgetSocket( &sockV6 );
6628 if( err ) exit( 1 );
6629 }
6630
6631 //===========================================================================================================================
6632 // MDNSColliderCmd
6633 //===========================================================================================================================
6634
6635 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
6636
6637 static void MDNSColliderCmd( void )
6638 {
6639 OSStatus err;
6640 MDNSColliderRef collider = NULL;
6641 uint8_t * rdataPtr = NULL;
6642 size_t rdataLen = 0;
6643 const char * ifname;
6644 uint32_t ifIndex;
6645 MDNSColliderProtocols protocols;
6646 uint16_t type;
6647 char ifName[ IF_NAMESIZE + 1 ];
6648 uint8_t name[ kDomainNameLengthMax ];
6649
6650 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6651 require_noerr_quiet( err, exit );
6652
6653 ifname = if_indextoname( ifIndex, ifName );
6654 if( !ifname )
6655 {
6656 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
6657 err = kNameErr;
6658 goto exit;
6659 }
6660
6661 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
6662 if( err )
6663 {
6664 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
6665 goto exit;
6666 }
6667
6668 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
6669 require_noerr_quiet( err, exit );
6670
6671 if( gMDNSCollider_RecordData )
6672 {
6673 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
6674 require_noerr_quiet( err, exit );
6675 }
6676
6677 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
6678 require_noerr( err, exit );
6679
6680 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
6681 if( err )
6682 {
6683 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
6684 goto exit;
6685 }
6686
6687 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
6688 require_noerr( err, exit );
6689 ForgetMem( &rdataPtr );
6690
6691 protocols = kMDNSColliderProtocol_None;
6692 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
6693 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
6694 MDNSColliderSetProtocols( collider, protocols );
6695 MDNSColliderSetInterfaceIndex( collider, ifIndex );
6696 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
6697
6698 err = MDNSColliderStart( collider );
6699 require_noerr( err, exit );
6700
6701 dispatch_main();
6702
6703 exit:
6704 FreeNullSafe( rdataPtr );
6705 CFReleaseNullSafe( collider );
6706 if( err ) exit( 1 );
6707 }
6708
6709 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
6710 {
6711 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
6712
6713 CFRelease( collider );
6714 exit( inError ? 1 : 0 );
6715 }
6716
6717 //===========================================================================================================================
6718 // MDNSQueryPrintPrologue
6719 //===========================================================================================================================
6720
6721 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6722 {
6723 const int receiveSecs = inContext->receiveSecs;
6724
6725 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6726 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6727 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6728 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6729 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6730 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6731 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6732 FPrintF( stdout, "Receive duration: " );
6733 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6734 else FPrintF( stdout, "∞\n" );
6735 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6736 }
6737
6738 //===========================================================================================================================
6739 // MDNSQueryReadHandler
6740 //===========================================================================================================================
6741
6742 static void MDNSQueryReadHandler( void *inContext )
6743 {
6744 OSStatus err;
6745 struct timeval now;
6746 SocketContext * const sockCtx = (SocketContext *) inContext;
6747 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
6748 size_t msgLen;
6749 sockaddr_ip fromAddr;
6750 Boolean foundAnswer = false;
6751
6752 gettimeofday( &now, NULL );
6753
6754 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6755 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6756 require_noerr( err, exit );
6757
6758 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6759 {
6760 const uint8_t * ptr;
6761 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6762 unsigned int rrCount, i;
6763 uint16_t type, class;
6764 uint8_t name[ kDomainNameLengthMax ];
6765
6766 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6767 require_noerr( err, exit );
6768
6769 if( context->qname[ 0 ] == 0 )
6770 {
6771 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6772 require_noerr( err, exit );
6773 }
6774
6775 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6776 for( i = 0; i < rrCount; ++i )
6777 {
6778 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6779 require_noerr( err, exit );
6780
6781 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6782 DomainNameEqual( name, context->qname ) )
6783 {
6784 foundAnswer = true;
6785 break;
6786 }
6787 }
6788 }
6789 if( context->allResponses || foundAnswer )
6790 {
6791 FPrintF( stdout, "---\n" );
6792 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6793 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6794 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6795 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
6796 }
6797
6798 exit:
6799 if( err ) exit( 1 );
6800 }
6801
6802 //===========================================================================================================================
6803 // PIDToUUIDCmd
6804 //===========================================================================================================================
6805
6806 static void PIDToUUIDCmd( void )
6807 {
6808 OSStatus err;
6809 int n;
6810 struct proc_uniqidentifierinfo info;
6811
6812 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6813 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6814
6815 FPrintF( stdout, "%#U\n", info.p_uuid );
6816 err = kNoErr;
6817
6818 exit:
6819 if( err ) exit( 1 );
6820 }
6821
6822 //===========================================================================================================================
6823 // DNSServerCmd
6824 //===========================================================================================================================
6825
6826 typedef struct DNSServerPrivate * DNSServerRef;
6827
6828 typedef struct
6829 {
6830 DNSServerRef server; // Reference to the DNS server.
6831 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
6832 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
6833 const char * domainOverride; // If non-NULL, the server is to use this domain instead of "d.test.".
6834 #if( TARGET_OS_DARWIN )
6835 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
6836 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
6837 Boolean addedResolver; // True if system DNS settings contains a resolver entry for server.
6838 #endif
6839 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
6840
6841 } DNSServerCmdContext;
6842
6843 typedef enum
6844 {
6845 kDNSServerEvent_Started = 1,
6846 kDNSServerEvent_Stopped = 2
6847
6848 } DNSServerEventType;
6849
6850 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6851
6852 CFTypeID DNSServerGetTypeID( void );
6853 static OSStatus
6854 DNSServerCreate(
6855 dispatch_queue_t inQueue,
6856 DNSServerEventHandler_f inEventHandler,
6857 void * inEventContext,
6858 unsigned int inResponseDelayMs,
6859 uint32_t inDefaultTTL,
6860 int inPort,
6861 Boolean inLoopbackOnly,
6862 const char * inDomain,
6863 Boolean inBadUDPMode,
6864 DNSServerRef * outServer );
6865 static void DNSServerStart( DNSServerRef inServer );
6866 static void DNSServerStop( DNSServerRef inServer );
6867
6868 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6869
6870 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
6871 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6872 static void DNSServerCmdSigIntHandler( void *inContext );
6873 static void DNSServerCmdSigTermHandler( void *inContext );
6874 #if( TARGET_OS_DARWIN )
6875 static void DNSServerCmdFollowedProcessHandler( void *inContext );
6876 #endif
6877
6878 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
6879 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6880
6881 static void DNSServerCmd( void )
6882 {
6883 OSStatus err;
6884 DNSServerCmdContext * context = NULL;
6885
6886 if( gDNSServer_Foreground )
6887 {
6888 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6889 }
6890
6891 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
6892 require_noerr_quiet( err, exit );
6893
6894 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
6895 require_noerr_quiet( err, exit );
6896
6897 err = CheckIntegerArgument( gDNSServer_Port, "port number", -UINT16_MAX, UINT16_MAX );
6898 require_noerr_quiet( err, exit );
6899
6900 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
6901 require_action( context, exit, err = kNoMemoryErr );
6902
6903 context->domainOverride = gDNSServer_DomainOverride;
6904 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
6905
6906 #if( TARGET_OS_DARWIN )
6907 if( gDNSServer_FollowPID )
6908 {
6909 context->followPID = _StringToPID( gDNSServer_FollowPID, &err );
6910 if( err || ( context->followPID < 0 ) )
6911 {
6912 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
6913 err = kParamErr;
6914 goto exit;
6915 }
6916
6917 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
6918 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
6919 require_noerr( err, exit );
6920 dispatch_resume( context->processMonitor );
6921 }
6922 else
6923 {
6924 context->followPID = -1;
6925 }
6926 #endif
6927
6928 signal( SIGINT, SIG_IGN );
6929 err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
6930 require_noerr( err, exit );
6931 dispatch_resume( context->sigIntSource );
6932
6933 signal( SIGTERM, SIG_IGN );
6934 err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
6935 require_noerr( err, exit );
6936 dispatch_resume( context->sigTermSource );
6937
6938 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context,
6939 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, gDNSServer_Port, context->loopbackOnly,
6940 context->domainOverride, gDNSServer_BadUDPMode ? true : false, &context->server );
6941 require_noerr( err, exit );
6942
6943 DNSServerStart( context->server );
6944 dispatch_main();
6945
6946 exit:
6947 FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
6948 if( context ) DNSServerCmdContextFree( context );
6949 if( err ) exit( 1 );
6950 }
6951
6952 //===========================================================================================================================
6953 // DNSServerCmdContextFree
6954 //===========================================================================================================================
6955
6956 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
6957 {
6958 ForgetCF( &inContext->server );
6959 dispatch_source_forget( &inContext->sigIntSource );
6960 dispatch_source_forget( &inContext->sigTermSource );
6961 #if( TARGET_OS_DARWIN )
6962 dispatch_source_forget( &inContext->processMonitor );
6963 #endif
6964 free( inContext );
6965 }
6966
6967 //===========================================================================================================================
6968 // DNSServerCmdEventHandler
6969 //===========================================================================================================================
6970
6971 #if( TARGET_OS_DARWIN )
6972 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort );
6973 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
6974 #endif
6975
6976 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext )
6977 {
6978 OSStatus err;
6979 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
6980
6981 if( inType == kDNSServerEvent_Started )
6982 {
6983 #if( TARGET_OS_DARWIN )
6984 const int port = (int) inEventData;
6985
6986 err = _DNSServerCmdLoopbackResolverAdd( context->domainOverride ? context->domainOverride : "d.test.", port );
6987 if( err )
6988 {
6989 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
6990 err );
6991 if( context->loopbackOnly ) ForgetDNSServer( &context->server );
6992 }
6993 else
6994 {
6995 context->addedResolver = true;
6996 }
6997 #endif
6998 }
6999 else if( inType == kDNSServerEvent_Stopped )
7000 {
7001 const OSStatus stopError = (OSStatus) inEventData;
7002
7003 if( stopError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", stopError );
7004
7005 err = kNoErr;
7006 #if( TARGET_OS_DARWIN )
7007 if( context->addedResolver )
7008 {
7009 err = _DNSServerCmdLoopbackResolverRemove();
7010 if( err )
7011 {
7012 ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7013 }
7014 else
7015 {
7016 context->addedResolver = false;
7017 }
7018 }
7019 else if( context->loopbackOnly )
7020 {
7021 err = kUnknownErr;
7022 }
7023 #endif
7024 DNSServerCmdContextFree( context );
7025 exit( ( stopError || err ) ? 1 : 0 );
7026 }
7027 }
7028
7029 #if( TARGET_OS_DARWIN )
7030 //===========================================================================================================================
7031 // _DNSServerCmdLoopbackResolverAdd
7032 //===========================================================================================================================
7033
7034 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort )
7035 {
7036 OSStatus err;
7037 SCDynamicStoreRef store;
7038 CFPropertyListRef plist = NULL;
7039 CFStringRef key = NULL;
7040 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7041 Boolean success;
7042
7043 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7044 err = map_scerror( store );
7045 require_noerr( err, exit );
7046
7047 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7048 "{"
7049 "%kO="
7050 "["
7051 "%s"
7052 "]"
7053 "%kO="
7054 "["
7055 "%.4a"
7056 "%.16a"
7057 "]"
7058 "%kO=%i"
7059 "%kO=%O"
7060 "%kO=%O"
7061 "}",
7062 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7063 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr,
7064 kSCPropNetDNSServerPort, inPort,
7065 kSCPropInterfaceName, CFSTR( "lo0" ),
7066 kSCPropNetDNSConfirmedServiceID, CFSTR( "com.apple.dnssdutil.server" ) );
7067 require_noerr( err, exit );
7068
7069 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7070 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7071 require_action( key, exit, err = kUnknownErr );
7072
7073 success = SCDynamicStoreSetValue( store, key, plist );
7074 require_action( success, exit, err = kUnknownErr );
7075
7076 exit:
7077 CFReleaseNullSafe( store );
7078 CFReleaseNullSafe( plist );
7079 CFReleaseNullSafe( key );
7080 return( err );
7081 }
7082
7083 //===========================================================================================================================
7084 // _DNSServerCmdLoopbackResolverRemove
7085 //===========================================================================================================================
7086
7087 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7088 {
7089 OSStatus err;
7090 SCDynamicStoreRef store;
7091 CFStringRef key = NULL;
7092 Boolean success;
7093
7094 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7095 err = map_scerror( store );
7096 require_noerr( err, exit );
7097
7098 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7099 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7100 require_action( key, exit, err = kUnknownErr );
7101
7102 success = SCDynamicStoreRemoveValue( store, key );
7103 require_action( success, exit, err = kUnknownErr );
7104
7105 exit:
7106 CFReleaseNullSafe( store );
7107 CFReleaseNullSafe( key );
7108 return( err );
7109 }
7110 #endif
7111
7112 //===========================================================================================================================
7113 // DNSServerCmdSigIntHandler
7114 //===========================================================================================================================
7115
7116 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal );
7117
7118 static void DNSServerCmdSigIntHandler( void *inContext )
7119 {
7120 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGINT );
7121 }
7122
7123 //===========================================================================================================================
7124 // DNSServerCmdSigTermHandler
7125 //===========================================================================================================================
7126
7127 static void DNSServerCmdSigTermHandler( void *inContext )
7128 {
7129 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGTERM );
7130 }
7131
7132 #if( TARGET_OS_DARWIN )
7133 //===========================================================================================================================
7134 // DNSServerCmdFollowedProcessHandler
7135 //===========================================================================================================================
7136
7137 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7138 {
7139 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7140
7141 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( context, 0 );
7142 }
7143 #endif
7144
7145 //===========================================================================================================================
7146 // _DNSServerCmdExternalExit
7147 //===========================================================================================================================
7148
7149 #define SignalNumberToString( X ) ( \
7150 ( (X) == SIGINT ) ? "SIGINT" : \
7151 ( (X) == SIGTERM ) ? "SIGTERM" : \
7152 "???" )
7153
7154 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal )
7155 {
7156 dispatch_source_forget( &inContext->sigIntSource );
7157 dispatch_source_forget( &inContext->sigTermSource );
7158 #if( TARGET_OS_DARWIN )
7159 dispatch_source_forget( &inContext->processMonitor );
7160
7161 if( inSignal == 0 )
7162 {
7163 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7164 }
7165 else
7166 #endif
7167 {
7168 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7169 }
7170
7171 ForgetDNSServer( &inContext->server );
7172 }
7173
7174 //===========================================================================================================================
7175 // DNSServerCreate
7176 //===========================================================================================================================
7177
7178 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7179
7180 typedef struct DNSDelayedResponse DNSDelayedResponse;
7181 struct DNSDelayedResponse
7182 {
7183 DNSDelayedResponse * next;
7184 sockaddr_ip destAddr;
7185 uint64_t targetTicks;
7186 uint8_t * msgPtr;
7187 size_t msgLen;
7188 };
7189
7190 struct DNSServerPrivate
7191 {
7192 CFRuntimeBase base; // CF object base.
7193 uint8_t * domain; // Parent domain of server's resource records.
7194 dispatch_queue_t queue; // Queue for DNS server's events.
7195 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7196 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7197 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7198 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7199 SocketRef sockUDPv4;
7200 SocketRef sockUDPv6;
7201 DNSServerEventHandler_f eventHandler;
7202 void * eventContext;
7203 DNSDelayedResponse * responseList;
7204 dispatch_source_t responseTimer;
7205 unsigned int responseDelayMs;
7206 uint32_t defaultTTL;
7207 uint32_t serial; // Serial number for SOA record.
7208 int port; // Port to use for receiving and sending DNS messages.
7209 OSStatus stopError;
7210 Boolean stopped;
7211 Boolean loopbackOnly;
7212 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
7213 };
7214
7215 static void _DNSServerUDPReadHandler( void *inContext );
7216 static void _DNSServerTCPReadHandler( void *inContext );
7217 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse );
7218 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList );
7219
7220 CF_CLASS_DEFINE( DNSServer );
7221
7222 static OSStatus
7223 DNSServerCreate(
7224 dispatch_queue_t inQueue,
7225 DNSServerEventHandler_f inEventHandler,
7226 void * inEventContext,
7227 unsigned int inResponseDelayMs,
7228 uint32_t inDefaultTTL,
7229 int inPort,
7230 Boolean inLoopbackOnly,
7231 const char * inDomain,
7232 Boolean inBadUDPMode,
7233 DNSServerRef * outServer )
7234 {
7235 OSStatus err;
7236 DNSServerRef obj = NULL;
7237
7238 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
7239
7240 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7241
7242 ReplaceDispatchQueue( &obj->queue, inQueue );
7243 obj->eventHandler = inEventHandler;
7244 obj->eventContext = inEventContext;
7245 obj->responseDelayMs = inResponseDelayMs;
7246 obj->defaultTTL = inDefaultTTL;
7247 obj->port = inPort;
7248 obj->loopbackOnly = inLoopbackOnly;
7249 obj->badUDPMode = inBadUDPMode;
7250
7251 if( inDomain )
7252 {
7253 err = StringToDomainName( inDomain, &obj->domain, NULL );
7254 require_noerr_quiet( err, exit );
7255 }
7256 else
7257 {
7258 err = DomainNameDup( kDDotTestDomainName, &obj->domain, NULL );
7259 require_noerr_quiet( err, exit );
7260 }
7261
7262 *outServer = obj;
7263 obj = NULL;
7264 err = kNoErr;
7265
7266 exit:
7267 CFReleaseNullSafe( obj );
7268 return( err );
7269 }
7270
7271 //===========================================================================================================================
7272 // _DNSServerFinalize
7273 //===========================================================================================================================
7274
7275 static void _DNSServerFinalize( CFTypeRef inObj )
7276 {
7277 DNSServerRef const me = (DNSServerRef) inObj;
7278
7279 check( !me->readSourceUDPv4 );
7280 check( !me->readSourceUDPv6 );
7281 check( !me->readSourceTCPv4 );
7282 check( !me->readSourceTCPv6 );
7283 check( !me->responseTimer );
7284 ForgetMem( &me->domain );
7285 dispatch_forget( &me->queue );
7286 }
7287
7288 //===========================================================================================================================
7289 // DNSServerStart
7290 //===========================================================================================================================
7291
7292 static void _DNSServerStart( void *inContext );
7293 static void _DNSServerStop( void *inContext, OSStatus inError );
7294
7295 static void DNSServerStart( DNSServerRef me )
7296 {
7297 CFRetain( me );
7298 dispatch_async_f( me->queue, me, _DNSServerStart );
7299 }
7300
7301 static void _DNSServerStart( void *inContext )
7302 {
7303 OSStatus err;
7304 struct timeval now;
7305 DNSServerRef const me = (DNSServerRef) inContext;
7306 SocketRef sock = kInvalidSocketRef;
7307 SocketContext * sockCtx = NULL;
7308 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7309 int year, month, day;
7310
7311 // Create IPv4 UDP socket.
7312 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7313 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7314 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7315 // used for the remaining sockets.
7316
7317 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7318 me->port, &me->port, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7319 require_noerr( err, exit );
7320 check( me->port > 0 );
7321
7322 // Create read source for IPv4 UDP socket.
7323
7324 err = SocketContextCreate( sock, me, &sockCtx );
7325 require_noerr( err, exit );
7326 sock = kInvalidSocketRef;
7327
7328 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7329 &me->readSourceUDPv4 );
7330 require_noerr( err, exit );
7331 dispatch_resume( me->readSourceUDPv4 );
7332 me->sockUDPv4 = sockCtx->sock;
7333 sockCtx = NULL;
7334
7335 // Create IPv6 UDP socket.
7336
7337 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7338 me->port, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7339 require_noerr( err, exit );
7340
7341 // Create read source for IPv6 UDP socket.
7342
7343 err = SocketContextCreate( sock, me, &sockCtx );
7344 require_noerr( err, exit );
7345 sock = kInvalidSocketRef;
7346
7347 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7348 &me->readSourceUDPv6 );
7349 require_noerr( err, exit );
7350 dispatch_resume( me->readSourceUDPv6 );
7351 me->sockUDPv6 = sockCtx->sock;
7352 sockCtx = NULL;
7353
7354 // Create IPv4 TCP socket.
7355
7356 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7357 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7358 require_noerr( err, exit );
7359
7360 // Create read source for IPv4 TCP socket.
7361
7362 err = SocketContextCreate( sock, me, &sockCtx );
7363 require_noerr( err, exit );
7364 sock = kInvalidSocketRef;
7365
7366 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7367 &me->readSourceTCPv4 );
7368 require_noerr( err, exit );
7369 dispatch_resume( me->readSourceTCPv4 );
7370 sockCtx = NULL;
7371
7372 // Create IPv6 TCP socket.
7373
7374 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7375 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7376 require_noerr( err, exit );
7377
7378 // Create read source for IPv6 TCP socket.
7379
7380 err = SocketContextCreate( sock, me, &sockCtx );
7381 require_noerr( err, exit );
7382 sock = kInvalidSocketRef;
7383
7384 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7385 &me->readSourceTCPv6 );
7386 require_noerr( err, exit );
7387 dispatch_resume( me->readSourceTCPv6 );
7388 sockCtx = NULL;
7389
7390 ds_ulog( kLogLevelInfo, "Server is using port %d.\n", me->port );
7391 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, (uintptr_t) me->port, me->eventContext );
7392
7393 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7394 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7395
7396 gettimeofday( &now, NULL );
7397 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
7398 NULL, NULL, NULL );
7399 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
7400
7401 exit:
7402 ForgetSocket( &sock );
7403 if( sockCtx ) SocketContextRelease( sockCtx );
7404 if( err ) _DNSServerStop( me, err );
7405 }
7406
7407 //===========================================================================================================================
7408 // DNSServerStop
7409 //===========================================================================================================================
7410
7411 static void _DNSServerUserStop( void *inContext );
7412 static void _DNSServerStop2( void *inContext );
7413
7414 static void DNSServerStop( DNSServerRef me )
7415 {
7416 CFRetain( me );
7417 dispatch_async_f( me->queue, me, _DNSServerUserStop );
7418 }
7419
7420 static void _DNSServerUserStop( void *inContext )
7421 {
7422 DNSServerRef const me = (DNSServerRef) inContext;
7423
7424 _DNSServerStop( me, kNoErr );
7425 CFRelease( me );
7426 }
7427
7428 static void _DNSServerStop( void *inContext, OSStatus inError )
7429 {
7430 DNSServerRef const me = (DNSServerRef) inContext;
7431
7432 me->stopError = inError;
7433 dispatch_source_forget( &me->readSourceUDPv4 );
7434 dispatch_source_forget( &me->readSourceUDPv6 );
7435 dispatch_source_forget( &me->readSourceTCPv4 );
7436 dispatch_source_forget( &me->readSourceTCPv6 );
7437 dispatch_source_forget( &me->responseTimer );
7438 me->sockUDPv4 = kInvalidSocketRef;
7439 me->sockUDPv6 = kInvalidSocketRef;
7440
7441 if( me->responseList )
7442 {
7443 _DNSDelayedResponseFreeList( me->responseList );
7444 me->responseList = NULL;
7445 }
7446 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7447 }
7448
7449 static void _DNSServerStop2( void *inContext )
7450 {
7451 DNSServerRef const me = (DNSServerRef) inContext;
7452
7453 if( !me->stopped )
7454 {
7455 me->stopped = true;
7456 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, (uintptr_t) me->stopError, me->eventContext );
7457 CFRelease( me );
7458 }
7459 CFRelease( me );
7460 }
7461
7462 //===========================================================================================================================
7463 // _DNSDelayedResponseFree
7464 //===========================================================================================================================
7465
7466 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse )
7467 {
7468 ForgetMem( &inResponse->msgPtr );
7469 free( inResponse );
7470 }
7471
7472 //===========================================================================================================================
7473 // _DNSDelayedResponseFreeList
7474 //===========================================================================================================================
7475
7476 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList )
7477 {
7478 DNSDelayedResponse * response;
7479
7480 while( ( response = inList ) != NULL )
7481 {
7482 inList = response->next;
7483 _DNSDelayedResponseFree( response );
7484 }
7485 }
7486
7487 //===========================================================================================================================
7488 // _DNSServerUDPReadHandler
7489 //===========================================================================================================================
7490
7491 static OSStatus
7492 _DNSServerAnswerQuery(
7493 DNSServerRef inServer,
7494 const uint8_t * inQueryPtr,
7495 size_t inQueryLen,
7496 Boolean inForTCP,
7497 uint8_t ** outResponsePtr,
7498 size_t * outResponseLen );
7499
7500 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7501 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7502
7503 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7504 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7505
7506 static OSStatus
7507 _DNSServerScheduleDelayedResponse(
7508 DNSServerRef inServer,
7509 const struct sockaddr * inDestAddr,
7510 uint8_t * inMsgPtr,
7511 size_t inMsgLen );
7512 static void _DNSServerUDPDelayedSend( void *inContext );
7513
7514 static void _DNSServerUDPReadHandler( void *inContext )
7515 {
7516 OSStatus err;
7517 SocketContext * const sockCtx = (SocketContext *) inContext;
7518 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7519 struct timeval now;
7520 ssize_t n;
7521 sockaddr_ip clientAddr;
7522 socklen_t clientAddrLen;
7523 uint8_t * responsePtr = NULL; // malloc'd
7524 size_t responseLen;
7525 uint8_t msg[ 512 ];
7526
7527 gettimeofday( &now, NULL );
7528
7529 // Receive message.
7530
7531 clientAddrLen = (socklen_t) sizeof( clientAddr );
7532 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7533 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7534 require_noerr( err, exit );
7535
7536 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7537
7538 if( n < kDNSHeaderLength )
7539 {
7540 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7541 goto exit;
7542 }
7543
7544 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7545
7546 // Create response.
7547
7548 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, &responsePtr, &responseLen );
7549 require_noerr_quiet( err, exit );
7550
7551 // Schedule response.
7552
7553 if( me->responseDelayMs > 0 )
7554 {
7555 err = _DNSServerScheduleDelayedResponse( me, &clientAddr.sa, responsePtr, responseLen );
7556 require_noerr( err, exit );
7557 responsePtr = NULL;
7558 }
7559 else
7560 {
7561 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7562
7563 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7564 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7565 require_noerr( err, exit );
7566 }
7567
7568 exit:
7569 FreeNullSafe( responsePtr );
7570 return;
7571 }
7572
7573 static OSStatus
7574 _DNSServerScheduleDelayedResponse(
7575 DNSServerRef me,
7576 const struct sockaddr * inDestAddr,
7577 uint8_t * inMsgPtr,
7578 size_t inMsgLen )
7579 {
7580 OSStatus err;
7581 DNSDelayedResponse * response;
7582 DNSDelayedResponse ** responsePtr;
7583 DNSDelayedResponse * newResponse;
7584 uint64_t targetTicks;
7585
7586 targetTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
7587
7588 newResponse = (DNSDelayedResponse *) calloc( 1, sizeof( *newResponse ) );
7589 require_action( newResponse, exit, err = kNoMemoryErr );
7590
7591 if( !me->responseList || ( targetTicks < me->responseList->targetTicks ) )
7592 {
7593 dispatch_source_forget( &me->responseTimer );
7594
7595 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7596 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, _DNSServerUDPDelayedSend,
7597 NULL, me, &me->responseTimer );
7598 require_noerr( err, exit );
7599 dispatch_resume( me->responseTimer );
7600 }
7601
7602 SockAddrCopy( inDestAddr, &newResponse->destAddr );
7603 newResponse->targetTicks = targetTicks;
7604 newResponse->msgPtr = inMsgPtr;
7605 newResponse->msgLen = inMsgLen;
7606
7607 for( responsePtr = &me->responseList; ( response = *responsePtr ) != NULL; responsePtr = &response->next )
7608 {
7609 if( newResponse->targetTicks < response->targetTicks ) break;
7610 }
7611 newResponse->next = response;
7612 *responsePtr = newResponse;
7613 newResponse = NULL;
7614 err = kNoErr;
7615
7616 exit:
7617 if( newResponse ) _DNSDelayedResponseFree( newResponse );
7618 return( err );
7619 }
7620
7621 static void _DNSServerUDPDelayedSend( void *inContext )
7622 {
7623 OSStatus err;
7624 DNSServerRef const me = (DNSServerRef) inContext;
7625 DNSDelayedResponse * response;
7626 SocketRef sock;
7627 ssize_t n;
7628 uint64_t nowTicks;
7629 uint64_t remainingNs;
7630 DNSDelayedResponse * freeList = NULL;
7631
7632 dispatch_source_forget( &me->responseTimer );
7633
7634 nowTicks = UpTicks();
7635 while( ( ( response = me->responseList ) != NULL ) && ( response->targetTicks <= nowTicks ) )
7636 {
7637 me->responseList = response->next;
7638
7639 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7640 response->msgLen, response->msgPtr, response->msgLen );
7641
7642 sock = ( response->destAddr.sa.sa_family == AF_INET ) ? me->sockUDPv4 : me->sockUDPv6;
7643 n = sendto( sock, (char *) response->msgPtr, response->msgLen, 0, &response->destAddr.sa,
7644 SockAddrGetSize( &response->destAddr ) );
7645 err = map_socket_value_errno( sock, n == (ssize_t) response->msgLen, n );
7646 check_noerr( err );
7647
7648 response->next = freeList;
7649 freeList = response;
7650 nowTicks = UpTicks();
7651 }
7652
7653 if( response )
7654 {
7655 check( response->targetTicks > nowTicks );
7656 remainingNs = UpTicksToNanoseconds( response->targetTicks - nowTicks );
7657 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7658
7659 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7660 me->queue, _DNSServerUDPDelayedSend, NULL, me, &me->responseTimer );
7661 require_noerr( err, exit );
7662 dispatch_resume( me->responseTimer );
7663 }
7664
7665 exit:
7666 if( freeList ) _DNSDelayedResponseFreeList( freeList );
7667 }
7668
7669 //===========================================================================================================================
7670 // _DNSServerAnswerQuery
7671 //===========================================================================================================================
7672
7673 #define kLabelPrefix_Alias "alias"
7674 #define kLabelPrefix_AliasTTL "alias-ttl"
7675 #define kLabelPrefix_Count "count-"
7676 #define kLabelPrefix_Tag "tag-"
7677 #define kLabelPrefix_TTL "ttl-"
7678 #define kLabel_IPv4 "ipv4"
7679 #define kLabel_IPv6 "ipv6"
7680 #define kLabelPrefix_SRV "srv-"
7681
7682 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7683 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7684
7685 typedef struct
7686 {
7687 uint16_t priority; // Priority from SRV label.
7688 uint16_t weight; // Weight from SRV label.
7689 uint16_t port; // Port number from SRV label.
7690 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
7691 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
7692
7693 } ParsedSRV;
7694
7695 static OSStatus
7696 _DNSServerInitializeResponseMessage(
7697 DataBuffer * inDB,
7698 unsigned int inID,
7699 unsigned int inFlags,
7700 const uint8_t * inQName,
7701 unsigned int inQType,
7702 unsigned int inQClass );
7703 static OSStatus
7704 _DNSServerAnswerQueryDynamically(
7705 DNSServerRef inServer,
7706 const uint8_t * inQName,
7707 unsigned int inQType,
7708 unsigned int inQClass,
7709 Boolean inForTCP,
7710 DataBuffer * inDB );
7711 static Boolean
7712 _DNSServerNameIsSRVName(
7713 DNSServerRef inServer,
7714 const uint8_t * inName,
7715 const uint8_t ** outDomainPtr,
7716 size_t * outDomainLen,
7717 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
7718 size_t * outSRVCount );
7719 static Boolean
7720 _DNSServerNameIsHostname(
7721 DNSServerRef inServer,
7722 const uint8_t * inName,
7723 uint32_t * outAliasCount,
7724 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
7725 size_t * outAliasTTLCount,
7726 unsigned int * outCount,
7727 unsigned int * outRandCount,
7728 uint32_t * outTTL,
7729 Boolean * outHasA,
7730 Boolean * outHasAAAA,
7731 Boolean * outHasSOA );
7732
7733 static OSStatus
7734 _DNSServerAnswerQuery(
7735 DNSServerRef me,
7736 const uint8_t * const inQueryPtr,
7737 const size_t inQueryLen,
7738 Boolean inForTCP,
7739 uint8_t ** outResponsePtr,
7740 size_t * outResponseLen )
7741 {
7742 OSStatus err;
7743 DataBuffer dataBuf;
7744 const uint8_t * ptr;
7745 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
7746 const DNSHeader * qhdr;
7747 const dns_fixed_fields_question * fields;
7748 unsigned int msgID, qflags, qtype, qclass, rflags;
7749 uint8_t qname[ kDomainNameLengthMax ];
7750
7751 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
7752
7753 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
7754
7755 qhdr = (const DNSHeader *) inQueryPtr;
7756 msgID = DNSHeaderGetID( qhdr );
7757 qflags = DNSHeaderGetFlags( qhdr );
7758
7759 // Minimal checking of the query message's header.
7760
7761 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
7762 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
7763 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
7764 {
7765 err = kRequestErr;
7766 goto exit;
7767 }
7768
7769 // Get QNAME.
7770
7771 ptr = (const uint8_t *) &qhdr[ 1 ];
7772 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
7773 require_noerr( err, exit );
7774
7775 // Get QTYPE and QCLASS.
7776
7777 require_action_quiet( ( (size_t)( queryEnd - ptr ) ) >= sizeof( *fields ), exit, err = kUnderrunErr );
7778 fields = (const dns_fixed_fields_question *) ptr;
7779 qtype = dns_fixed_fields_question_get_type( fields );
7780 qclass = dns_fixed_fields_question_get_class( fields );
7781
7782 // Create a tentative response message.
7783
7784 rflags = kDNSHeaderFlag_Response;
7785 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
7786 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
7787
7788 if( me->badUDPMode && !inForTCP ) msgID = (uint16_t)( msgID + 1 );
7789 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7790 require_noerr( err, exit );
7791
7792 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inForTCP, &dataBuf );
7793 if( err )
7794 {
7795 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
7796 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7797 require_noerr( err, exit );
7798 }
7799
7800 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
7801 require_noerr( err, exit );
7802
7803 exit:
7804 DataBuffer_Free( &dataBuf );
7805 return( err );
7806 }
7807
7808 static OSStatus
7809 _DNSServerInitializeResponseMessage(
7810 DataBuffer * inDB,
7811 unsigned int inID,
7812 unsigned int inFlags,
7813 const uint8_t * inQName,
7814 unsigned int inQType,
7815 unsigned int inQClass )
7816 {
7817 OSStatus err;
7818 DNSHeader header;
7819
7820 DataBuffer_Reset( inDB );
7821
7822 memset( &header, 0, sizeof( header ) );
7823 DNSHeaderSetID( &header, inID );
7824 DNSHeaderSetFlags( &header, inFlags );
7825 DNSHeaderSetQuestionCount( &header, 1 );
7826
7827 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
7828 require_noerr( err, exit );
7829
7830 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), (uint16_t) inQType,
7831 (uint16_t) inQClass );
7832 require_noerr( err, exit );
7833
7834 exit:
7835 return( err );
7836 }
7837
7838 static OSStatus
7839 _DNSServerAnswerQueryDynamically(
7840 DNSServerRef me,
7841 const uint8_t * const inQName,
7842 const unsigned int inQType,
7843 const unsigned int inQClass,
7844 const Boolean inForTCP,
7845 DataBuffer * const inDB )
7846 {
7847 OSStatus err;
7848 DNSHeader * hdr;
7849 unsigned int flags, rcode;
7850 uint32_t aliasCount, i;
7851 uint32_t aliasTTLs[ kMaxAliasTTLCount ];
7852 size_t aliasTTLCount;
7853 unsigned int addrCount, randCount;
7854 uint32_t ttl;
7855 ParsedSRV srvArray[ kMaxParsedSRVCount ];
7856 size_t srvCount;
7857 const uint8_t * srvDomainPtr;
7858 size_t srvDomainLen;
7859 unsigned int answerCount;
7860 Boolean notImplemented, truncated;
7861 Boolean useAliasTTLs, nameExists, nameHasA, nameHasAAAA, nameHasSRV, nameHasSOA;
7862 uint8_t namePtr[ 2 ];
7863 dns_fixed_fields_record fields;
7864
7865 answerCount = 0;
7866 truncated = false;
7867 nameExists = false;
7868 require_action_quiet( inQClass == kDNSServiceClass_IN, done, notImplemented = true );
7869
7870 notImplemented = false;
7871 aliasCount = 0;
7872 nameHasA = false;
7873 nameHasAAAA = false;
7874 nameHasSOA = false;
7875 useAliasTTLs = false;
7876 nameHasSRV = false;
7877 srvDomainLen = 0;
7878 srvCount = 0;
7879
7880 if( _DNSServerNameIsHostname( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount, &ttl,
7881 &nameHasA, &nameHasAAAA, &nameHasSOA ) )
7882 {
7883 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
7884 check( ( addrCount >= 1 ) && ( addrCount <= 255 ) );
7885 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
7886 check( nameHasA || nameHasAAAA );
7887
7888 if( aliasTTLCount > 0 )
7889 {
7890 aliasCount = (uint32_t) aliasTTLCount;
7891 useAliasTTLs = true;
7892 }
7893 nameExists = true;
7894 }
7895 else if( _DNSServerNameIsSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) )
7896 {
7897 nameHasSRV = true;
7898 nameExists = true;
7899 }
7900 require_quiet( nameExists, done );
7901
7902 if( aliasCount > 0 )
7903 {
7904 size_t nameOffset;
7905 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
7906
7907 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7908 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7909 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7910 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7911
7912 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
7913
7914 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7915
7916 nameOffset = kDNSHeaderLength;
7917
7918 for( i = aliasCount; i >= 1; --i )
7919 {
7920 size_t nameLen;
7921 size_t rdataLen;
7922 uint32_t j;
7923 uint32_t aliasTTL;
7924 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
7925
7926 if( nameOffset <= kDNSCompressionOffsetMax )
7927 {
7928 DNSMessageWriteLabelPointer( namePtr, nameOffset );
7929 nameLen = sizeof( namePtr );
7930 }
7931 else
7932 {
7933 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
7934 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
7935 }
7936
7937 if( i >= 2 )
7938 {
7939 char * dst = (char *) &rdataLabel[ 1 ];
7940 char * const lim = (char *) &rdataLabel[ countof( rdataLabel ) ];
7941
7942 if( useAliasTTLs )
7943 {
7944 err = SNPrintF_Add( &dst, lim, kLabelPrefix_AliasTTL );
7945 require_noerr( err, exit );
7946
7947 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
7948 {
7949 err = SNPrintF_Add( &dst, lim, "-%u", aliasTTLs[ j ] );
7950 require_noerr( err, exit );
7951 }
7952 }
7953 else
7954 {
7955 err = SNPrintF_Add( &dst, lim, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
7956 require_noerr( err, exit );
7957 }
7958 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
7959 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
7960 }
7961 else
7962 {
7963 rdataLen = sizeof( superPtr );
7964 }
7965
7966 if( !inForTCP )
7967 {
7968 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
7969
7970 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
7971 {
7972 truncated = true;
7973 goto done;
7974 }
7975 }
7976 ++answerCount;
7977
7978 // Set CNAME record's NAME.
7979
7980 if( nameOffset <= kDNSCompressionOffsetMax )
7981 {
7982 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
7983 require_noerr( err, exit );
7984 }
7985 else
7986 {
7987 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
7988 require_noerr( err, exit );
7989
7990 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
7991 require_noerr( err, exit );
7992 }
7993
7994 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7995
7996 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
7997 dns_fixed_fields_record_init( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
7998 (uint16_t) rdataLen );
7999 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8000 require_noerr( err, exit );
8001
8002 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8003
8004 nameOffset = DataBuffer_GetLen( inDB );
8005
8006 // Set CNAME record's RDATA.
8007
8008 if( i >= 2 )
8009 {
8010 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
8011 require_noerr( err, exit );
8012 }
8013 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8014 require_noerr( err, exit );
8015 }
8016
8017 namePtr[ 0 ] = superPtr[ 0 ];
8018 namePtr[ 1 ] = superPtr[ 1 ];
8019 }
8020 else
8021 {
8022 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8023
8024 DNSMessageWriteLabelPointer( namePtr, kDNSHeaderLength );
8025 }
8026
8027 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
8028 {
8029 uint8_t * lsb; // Pointer to the least significant byte of record data.
8030 size_t recordLen; // Length of the entire record.
8031 size_t rdataLen; // Length of record's RDATA.
8032 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8033 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
8034 const int useBadAddrs = ( me->badUDPMode && !inForTCP ) ? true : false;
8035
8036 if( inQType == kDNSServiceType_A )
8037 {
8038 const uint32_t baseAddrV4 = useBadAddrs ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
8039
8040 require_quiet( nameHasA, done );
8041
8042 rdataLen = 4;
8043 WriteBig32( rdata, baseAddrV4 );
8044 lsb = &rdata[ 3 ];
8045 }
8046 else
8047 {
8048 const uint8_t * const baseAddrV6 = useBadAddrs ? kDNSServerBadBaseAddrV6 : kDNSServerBaseAddrV6;
8049
8050 require_quiet( nameHasAAAA, done );
8051
8052 rdataLen = 16;
8053 memcpy( rdata, baseAddrV6, 16 );
8054 lsb = &rdata[ 15 ];
8055 }
8056
8057 if( randCount > 0 )
8058 {
8059 // Populate the array with all integers between 1 and <randCount>, inclusive.
8060
8061 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
8062
8063 // Prevent dubious static analyzer warning.
8064 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8065 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8066 // for some reason.
8067
8068 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
8069 addrCount, randCount );
8070
8071 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8072 // 1 and <randCount>, inclusive.
8073 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8074 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8075
8076 for( i = 0; i < addrCount; ++i )
8077 {
8078 uint8_t tmp;
8079 uint32_t j;
8080
8081 j = RandomRange( i, randCount - 1 );
8082 if( i != j )
8083 {
8084 tmp = randIntegers[ i ];
8085 randIntegers[ i ] = randIntegers[ j ];
8086 randIntegers[ j ] = tmp;
8087 }
8088 }
8089 }
8090
8091 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8092 for( i = 0; i < addrCount; ++i )
8093 {
8094 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8095 {
8096 truncated = true;
8097 goto done;
8098 }
8099
8100 // Set record NAME.
8101
8102 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8103 require_noerr( err, exit );
8104
8105 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8106
8107 dns_fixed_fields_record_init( &fields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
8108 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8109 require_noerr( err, exit );
8110
8111 // Set record RDATA.
8112
8113 *lsb = ( randCount > 0 ) ? randIntegers[ i ] : ( *lsb + 1 );
8114
8115 err = DataBuffer_Append( inDB, rdata, rdataLen );
8116 require_noerr( err, exit );
8117
8118 ++answerCount;
8119 }
8120 }
8121 else if( inQType == kDNSServiceType_SRV )
8122 {
8123 require_quiet( nameHasSRV, done );
8124
8125 dns_fixed_fields_record_init( &fields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
8126
8127 for( i = 0; i < srvCount; ++i )
8128 {
8129 dns_fixed_fields_srv fieldsSRV;
8130 size_t rdataLen;
8131 size_t recordLen;
8132 const ParsedSRV * const srv = &srvArray[ i ];
8133
8134 rdataLen = sizeof( fieldsSRV ) + srvDomainLen + srv->targetLen + 1;
8135 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8136
8137 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8138 {
8139 truncated = true;
8140 goto done;
8141 }
8142
8143 // Append record NAME.
8144
8145 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8146 require_noerr( err, exit );
8147
8148 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8149
8150 WriteBig16( fields.rdlength, rdataLen );
8151 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8152 require_noerr( err, exit );
8153
8154 // Append SRV RDATA.
8155
8156 dns_fixed_fields_srv_init( &fieldsSRV, srv->priority, srv->weight, srv->port );
8157
8158 err = DataBuffer_Append( inDB, &fieldsSRV, sizeof( fieldsSRV ) );
8159 require_noerr( err, exit );
8160
8161 if( srv->targetLen > 0 )
8162 {
8163 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
8164 require_noerr( err, exit );
8165 }
8166
8167 if( srvDomainLen > 0 )
8168 {
8169 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
8170 require_noerr( err, exit );
8171 }
8172
8173 err = DataBuffer_Append( inDB, "", 1 ); // Append root label.
8174 require_noerr( err, exit );
8175
8176 ++answerCount;
8177 }
8178 }
8179 else if( inQType == kDNSServiceType_SOA )
8180 {
8181 size_t nameLen, recordLen;
8182
8183 require_quiet( nameHasSOA, done );
8184
8185 nameLen = DomainNameLength( me->domain );
8186 if( !inForTCP )
8187 {
8188 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
8189 require_noerr( err, exit );
8190
8191 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8192 {
8193 truncated = true;
8194 goto done;
8195 }
8196 }
8197
8198 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
8199 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
8200 me->defaultTTL, NULL );
8201 require_noerr( err, exit );
8202
8203 ++answerCount;
8204 }
8205
8206 done:
8207 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8208 flags = DNSHeaderGetFlags( hdr );
8209 if( notImplemented )
8210 {
8211 rcode = kDNSRCode_NotImplemented;
8212 }
8213 else
8214 {
8215 flags |= kDNSHeaderFlag_AuthAnswer;
8216 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8217 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8218 }
8219 DNSFlagsSetRCode( flags, rcode );
8220 DNSHeaderSetFlags( hdr, flags );
8221 DNSHeaderSetAnswerCount( hdr, answerCount );
8222 err = kNoErr;
8223
8224 exit:
8225 return( err );
8226 }
8227
8228 static Boolean
8229 _DNSServerNameIsHostname(
8230 DNSServerRef me,
8231 const uint8_t * inName,
8232 uint32_t * outAliasCount,
8233 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
8234 size_t * outAliasTTLCount,
8235 unsigned int * outCount,
8236 unsigned int * outRandCount,
8237 uint32_t * outTTL,
8238 Boolean * outHasA,
8239 Boolean * outHasAAAA,
8240 Boolean * outHasSOA )
8241 {
8242 OSStatus err;
8243 const uint8_t * label;
8244 const uint8_t * nextLabel;
8245 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8246 unsigned int count = 0; // First arg from Count label. Valid values are in [1, 255].
8247 unsigned int randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
8248 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8249 size_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
8250 int hasTagLabel = false;
8251 int hasIPv4Label = false;
8252 int hasIPv6Label = false;
8253 int isNameValid = false;
8254
8255 for( label = inName; label[ 0 ]; label = nextLabel )
8256 {
8257 uint32_t arg;
8258
8259 nextLabel = &label[ 1 + label[ 0 ] ];
8260
8261 // Check if the first label is a valid alias TTL sequence label.
8262
8263 if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_AliasTTL ) == 0 ) )
8264 {
8265 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_AliasTTL ) ];
8266 const char * const end = (const char *) nextLabel;
8267 const char * next;
8268
8269 check( label[ 0 ] <= kDomainLabelLengthMax );
8270
8271 while( ptr < end )
8272 {
8273 if( *ptr != '-' ) break;
8274 ++ptr;
8275 err = DecimalTextToUInt32( ptr, end, &arg, &next );
8276 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8277 inAliasTTLs[ aliasTTLCount++ ] = arg;
8278 ptr = next;
8279 }
8280 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
8281 }
8282
8283 // Check if the first label is a valid alias label.
8284
8285 else if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Alias ) == 0 ) )
8286 {
8287 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Alias ) ];
8288 const char * const end = (const char *) nextLabel;
8289
8290 if( ptr < end )
8291 {
8292 if( *ptr++ != '-' ) break;
8293 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8294 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
8295 aliasCount = arg;
8296 if( ptr != end ) break;
8297 }
8298 else
8299 {
8300 aliasCount = 1;
8301 }
8302 }
8303
8304 // Check if this label is a valid count label.
8305
8306 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Count ) == 0 )
8307 {
8308 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Count ) ];
8309 const char * const end = (const char *) nextLabel;
8310
8311 if( count > 0 ) break; // Count cannot be specified more than once.
8312
8313 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8314 if( err || ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be in [1, 255].
8315 count = (unsigned int) arg;
8316
8317 if( ptr < end )
8318 {
8319 if( *ptr++ != '-' ) break;
8320 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8321 if( err || ( arg < (uint32_t) count ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
8322 randCount = (unsigned int) arg;
8323 if( ptr != end ) break;
8324 }
8325 }
8326
8327 // Check if this label is a valid TTL label.
8328
8329 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_TTL ) == 0 )
8330 {
8331 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_TTL ) ];
8332 const char * const end = (const char *) nextLabel;
8333
8334 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
8335
8336 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8337 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8338 ttl = (int32_t) arg;
8339 if( ptr != end ) break;
8340 }
8341
8342 // Check if this label is a valid IPv4 label.
8343
8344 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv4 ) == 0 )
8345 {
8346 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8347 hasIPv4Label = true;
8348 }
8349
8350 // Check if this label is a valid IPv6 label.
8351
8352 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv6 ) == 0 )
8353 {
8354 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8355 hasIPv6Label = true;
8356 }
8357
8358 // Check if this label is a valid tag label.
8359
8360 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Tag ) == 0 )
8361 {
8362 hasTagLabel = true;
8363 }
8364
8365 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8366 // In both cases, there are no more labels to check.
8367
8368 else
8369 {
8370 if( DomainNameEqual( label, me->domain ) ) isNameValid = true;
8371 break;
8372 }
8373 }
8374 require_quiet( isNameValid, exit );
8375
8376 if( outAliasCount ) *outAliasCount = aliasCount;
8377 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
8378 if( outCount ) *outCount = ( count > 0 ) ? count : 1;
8379 if( outRandCount ) *outRandCount = randCount;
8380 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
8381 if( outHasA ) *outHasA = ( hasIPv4Label || !hasIPv6Label ) ? true : false;
8382 if( outHasAAAA ) *outHasAAAA = ( hasIPv6Label || !hasIPv4Label ) ? true : false;
8383 if( outHasSOA )
8384 {
8385 *outHasSOA = ( !count && ( ttl < 0 ) && !hasIPv4Label && !hasIPv6Label && !hasTagLabel ) ? true : false;
8386 }
8387
8388 exit:
8389 return( isNameValid ? true : false );
8390 }
8391
8392 static Boolean
8393 _DNSServerNameIsSRVName(
8394 DNSServerRef me,
8395 const uint8_t * inName,
8396 const uint8_t ** outDomainPtr,
8397 size_t * outDomainLen,
8398 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
8399 size_t * outSRVCount )
8400 {
8401 OSStatus err;
8402 const uint8_t * label;
8403 const uint8_t * domainPtr;
8404 size_t domainLen;
8405 size_t srvCount;
8406 uint32_t arg;
8407 int isNameValid = false;
8408
8409 label = inName;
8410
8411 // Ensure that first label, i.e, the service label, begins with a '_' character.
8412
8413 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8414 label = DomainNameGetNextLabel( label );
8415
8416 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8417
8418 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8419 label = DomainNameGetNextLabel( label );
8420
8421 // Parse the domain name, if any.
8422
8423 domainPtr = label;
8424 while( *label )
8425 {
8426 if( DomainNameEqual( label, me->domain ) ||
8427 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8428 label = DomainNameGetNextLabel( label );
8429 }
8430 require_quiet( *label, exit );
8431
8432 domainLen = (size_t)( label - domainPtr );
8433
8434 // Parse SRV labels, if any.
8435
8436 srvCount = 0;
8437 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
8438 {
8439 const uint8_t * const nextLabel = DomainNameGetNextLabel( label );
8440 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
8441 const char * const end = (const char *) nextLabel;
8442 const uint8_t * target;
8443 unsigned int priority, weight, port;
8444
8445 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8446 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8447 priority = (unsigned int) arg;
8448
8449 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8450 ++ptr;
8451
8452 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8453 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8454 weight = (unsigned int) arg;
8455
8456 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8457 ++ptr;
8458
8459 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8460 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8461 port = (unsigned int) arg;
8462
8463 require_quiet( ptr == end, exit );
8464
8465 target = nextLabel;
8466 for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
8467 {
8468 if( DomainNameEqual( label, me->domain ) ||
8469 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8470 }
8471 require_quiet( *label, exit );
8472
8473 if( inSRVArray )
8474 {
8475 inSRVArray[ srvCount ].priority = (uint16_t) priority;
8476 inSRVArray[ srvCount ].weight = (uint16_t) weight;
8477 inSRVArray[ srvCount ].port = (uint16_t) port;
8478 inSRVArray[ srvCount ].targetPtr = target;
8479 inSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
8480 }
8481 ++srvCount;
8482 }
8483 require_quiet( DomainNameEqual( label, me->domain ), exit );
8484 isNameValid = true;
8485
8486 if( outDomainPtr ) *outDomainPtr = domainPtr;
8487 if( outDomainLen ) *outDomainLen = domainLen;
8488 if( outSRVCount ) *outSRVCount = srvCount;
8489
8490 exit:
8491 return( isNameValid ? true : false );
8492 }
8493
8494 //===========================================================================================================================
8495 // _DNSServerTCPReadHandler
8496 //===========================================================================================================================
8497
8498 typedef struct
8499 {
8500 DNSServerRef server; // Reference to DNS server object.
8501 sockaddr_ip clientAddr; // Client's address.
8502 dispatch_source_t readSource; // Dispatch read source for client socket.
8503 dispatch_source_t writeSource; // Dispatch write source for client socket.
8504 size_t offset; // Offset into receive buffer.
8505 void * msgPtr; // Pointer to dynamically allocated message buffer.
8506 size_t msgLen; // Length of message buffer.
8507 Boolean readSuspended; // True if the read source is currently suspended.
8508 Boolean writeSuspended; // True if the write source is currently suspended.
8509 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8510 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8511 iovec_t iov[ 2 ]; // IO vector for writing response message.
8512 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8513 int iovCount; // Vector count for SocketWriteData().
8514
8515 } TCPConnectionContext;
8516
8517 static void TCPConnectionStop( TCPConnectionContext *inContext );
8518 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8519 static void TCPConnectionReadHandler( void *inContext );
8520 static void TCPConnectionWriteHandler( void *inContext );
8521
8522 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8523
8524 static void _DNSServerTCPReadHandler( void *inContext )
8525 {
8526 OSStatus err;
8527 SocketContext * const sockCtx = (SocketContext *) inContext;
8528 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
8529 TCPConnectionContext * connection;
8530 socklen_t clientAddrLen;
8531 SocketRef newSock = kInvalidSocketRef;
8532 SocketContext * newSockCtx = NULL;
8533
8534 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8535 require_action( connection, exit, err = kNoMemoryErr );
8536
8537 CFRetain( me );
8538 connection->server = me;
8539
8540 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8541 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8542 err = map_socket_creation_errno( newSock );
8543 require_noerr( err, exit );
8544
8545 err = SocketContextCreate( newSock, connection, &newSockCtx );
8546 require_noerr( err, exit );
8547 newSock = kInvalidSocketRef;
8548
8549 err = DispatchReadSourceCreate( newSockCtx->sock, me->queue, TCPConnectionReadHandler, SocketContextCancelHandler,
8550 newSockCtx, &connection->readSource );
8551 require_noerr( err, exit );
8552 SocketContextRetain( newSockCtx );
8553 dispatch_resume( connection->readSource );
8554
8555 err = DispatchWriteSourceCreate( newSockCtx->sock, me->queue, TCPConnectionWriteHandler, SocketContextCancelHandler,
8556 newSockCtx, &connection->writeSource );
8557 require_noerr( err, exit );
8558 SocketContextRetain( newSockCtx );
8559 connection->writeSuspended = true;
8560 connection = NULL;
8561
8562 exit:
8563 ForgetSocket( &newSock );
8564 SocketContextRelease( newSockCtx );
8565 TCPConnectionForget( &connection );
8566 }
8567
8568 //===========================================================================================================================
8569 // TCPConnectionStop
8570 //===========================================================================================================================
8571
8572 static void TCPConnectionStop( TCPConnectionContext *inContext )
8573 {
8574 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8575 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8576 }
8577
8578 //===========================================================================================================================
8579 // TCPConnectionContextFree
8580 //===========================================================================================================================
8581
8582 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8583 {
8584 check( !inContext->readSource );
8585 check( !inContext->writeSource );
8586 ForgetCF( &inContext->server );
8587 ForgetMem( &inContext->msgPtr );
8588 free( inContext );
8589 }
8590
8591 //===========================================================================================================================
8592 // TCPConnectionReadHandler
8593 //===========================================================================================================================
8594
8595 static void TCPConnectionReadHandler( void *inContext )
8596 {
8597 OSStatus err;
8598 SocketContext * const sockCtx = (SocketContext *) inContext;
8599 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8600 struct timeval now;
8601 uint8_t * responsePtr = NULL; // malloc'd
8602 size_t responseLen;
8603
8604 // Receive message length.
8605
8606 if( !connection->receivedLength )
8607 {
8608 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8609 if( err == EWOULDBLOCK ) goto exit;
8610 require_noerr( err, exit );
8611
8612 connection->offset = 0;
8613 connection->msgLen = ReadBig16( connection->lenBuf );
8614 connection->msgPtr = malloc( connection->msgLen );
8615 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8616 connection->receivedLength = true;
8617 }
8618
8619 // Receive message.
8620
8621 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8622 if( err == EWOULDBLOCK ) goto exit;
8623 require_noerr( err, exit );
8624
8625 gettimeofday( &now, NULL );
8626 dispatch_suspend( connection->readSource );
8627 connection->readSuspended = true;
8628
8629 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8630 connection->msgLen, &connection->clientAddr, &now );
8631
8632 if( connection->msgLen < kDNSHeaderLength )
8633 {
8634 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8635 goto exit;
8636 }
8637
8638 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8639
8640 // Create response.
8641
8642 err = _DNSServerAnswerQueryForTCP( connection->server, connection->msgPtr, connection->msgLen, &responsePtr,
8643 &responseLen );
8644 require_noerr_quiet( err, exit );
8645
8646 // Send response.
8647
8648 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8649
8650 free( connection->msgPtr );
8651 connection->msgPtr = responsePtr;
8652 connection->msgLen = responseLen;
8653 responsePtr = NULL;
8654
8655 check( connection->msgLen <= UINT16_MAX );
8656 WriteBig16( connection->lenBuf, connection->msgLen );
8657 connection->iov[ 0 ].iov_base = connection->lenBuf;
8658 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8659 connection->iov[ 1 ].iov_base = connection->msgPtr;
8660 connection->iov[ 1 ].iov_len = connection->msgLen;
8661
8662 connection->iovPtr = connection->iov;
8663 connection->iovCount = 2;
8664
8665 check( connection->writeSuspended );
8666 dispatch_resume( connection->writeSource );
8667 connection->writeSuspended = false;
8668
8669 exit:
8670 FreeNullSafe( responsePtr );
8671 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8672 }
8673
8674 //===========================================================================================================================
8675 // TCPConnectionWriteHandler
8676 //===========================================================================================================================
8677
8678 static void TCPConnectionWriteHandler( void *inContext )
8679 {
8680 OSStatus err;
8681 SocketContext * const sockCtx = (SocketContext *) inContext;
8682 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8683
8684 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8685 if( err == EWOULDBLOCK ) goto exit;
8686 check_noerr( err );
8687
8688 TCPConnectionForget( &connection );
8689
8690 exit:
8691 return;
8692 }
8693
8694 //===========================================================================================================================
8695 // MDNSReplierCmd
8696 //===========================================================================================================================
8697
8698 typedef struct
8699 {
8700 uint8_t * hostname; // Used as the base name for hostnames and service names.
8701 uint8_t * serviceLabel; // Label containing the base service name.
8702 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
8703 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
8704 size_t bitmapCount; // Number of 64-bit bitmaps.
8705 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
8706 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
8707 uint32_t ifIndex; // Index of the interface to run on.
8708 unsigned int recordCountA; // Number of A records per hostname.
8709 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
8710 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8711 double ucastDropRate; // Probability of dropping a unicast response.
8712 double mcastDropRate; // Probability of dropping a multicast query or response.
8713 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8714 Boolean noAdditionals; // True if responses are to not include additional records.
8715 Boolean useIPv4; // True if the replier is to use IPv4.
8716 Boolean useIPv6; // True if the replier is to use IPv6.
8717 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
8718 #if( TARGET_OS_DARWIN )
8719 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
8720 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
8721 #endif
8722
8723 } MDNSReplierContext;
8724
8725 typedef struct MRResourceRecord MRResourceRecord;
8726 struct MRResourceRecord
8727 {
8728 MRResourceRecord * next; // Next item in list.
8729 uint8_t * name; // Resource record name.
8730 uint16_t type; // Resource record type.
8731 uint16_t class; // Resource record class.
8732 uint32_t ttl; // Resource record TTL.
8733 uint16_t rdlength; // Resource record data length.
8734 uint8_t * rdata; // Resource record data.
8735 const uint8_t * target; // For SRV records, pointer to target in RDATA.
8736 };
8737
8738 typedef struct MRNameOffsetItem MRNameOffsetItem;
8739 struct MRNameOffsetItem
8740 {
8741 MRNameOffsetItem * next; // Next item in list.
8742 uint16_t offset; // Offset of domain name in response message.
8743 uint8_t name[ 1 ]; // Variable-length array for domain name.
8744 };
8745
8746 #if( TARGET_OS_DARWIN )
8747 static void _MDNSReplierFollowedProcessHandler( void *inContext );
8748 #endif
8749 static void _MDNSReplierReadHandler( void *inContext );
8750 static OSStatus
8751 _MDNSReplierAnswerQuery(
8752 MDNSReplierContext * inContext,
8753 const uint8_t * inQueryPtr,
8754 size_t inQueryLen,
8755 sockaddr_ip * inSender,
8756 SocketRef inSock,
8757 unsigned int inIndex );
8758 static OSStatus
8759 _MDNSReplierAnswerListAdd(
8760 MDNSReplierContext * inContext,
8761 MRResourceRecord ** inAnswerList,
8762 unsigned int inIndex,
8763 const uint8_t * inName,
8764 unsigned int inType,
8765 unsigned int inClass );
8766 static void
8767 _MDNSReplierAnswerListRemovePTR(
8768 MRResourceRecord ** inAnswerListPtr,
8769 const uint8_t * inName,
8770 const uint8_t * inRData );
8771 static OSStatus
8772 _MDNSReplierSendOrDropResponse(
8773 MDNSReplierContext * inContext,
8774 MRResourceRecord * inAnswerList,
8775 sockaddr_ip * inQuerier,
8776 SocketRef inSock,
8777 unsigned int inIndex,
8778 Boolean inUnicast );
8779 static OSStatus
8780 _MDNSReplierCreateResponse(
8781 MDNSReplierContext * inContext,
8782 MRResourceRecord * inAnswerList,
8783 unsigned int inIndex,
8784 uint8_t ** outResponsePtr,
8785 size_t * outResponseLen );
8786 static OSStatus
8787 _MDNSReplierAppendNameToResponse(
8788 DataBuffer * inResponse,
8789 const uint8_t * inName,
8790 MRNameOffsetItem ** inNameOffsetListPtr );
8791 static Boolean
8792 _MDNSReplierServiceTypeMatch(
8793 const MDNSReplierContext * inContext,
8794 const uint8_t * inName,
8795 unsigned int * outTXTSize,
8796 unsigned int * outCount );
8797 static Boolean
8798 _MDNSReplierServiceInstanceNameMatch(
8799 const MDNSReplierContext * inContext,
8800 const uint8_t * inName,
8801 unsigned int * outIndex,
8802 unsigned int * outTXTSize,
8803 unsigned int * outCount );
8804 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
8805 static Boolean
8806 _MDNSReplierHostnameMatch(
8807 const MDNSReplierContext * inContext,
8808 const uint8_t * inName,
8809 unsigned int * outIndex );
8810 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
8811 static OSStatus
8812 _MRResourceRecordCreate(
8813 uint8_t * inName,
8814 uint16_t inType,
8815 uint16_t inClass,
8816 uint32_t inTTL,
8817 uint16_t inRDLength,
8818 uint8_t * inRData,
8819 MRResourceRecord ** outRecord );
8820 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
8821 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
8822 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
8823 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
8824 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
8825
8826 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
8827 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8828
8829 static void MDNSReplierCmd( void )
8830 {
8831 OSStatus err;
8832 MDNSReplierContext * context;
8833 SocketRef sockV4 = kInvalidSocketRef;
8834 SocketRef sockV6 = kInvalidSocketRef;
8835 const char * ifname;
8836 size_t len;
8837 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
8838 char ifnameBuf[ IF_NAMESIZE + 1 ];
8839
8840 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
8841 require_noerr_quiet( err, exit );
8842
8843 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
8844 require_noerr_quiet( err, exit );
8845
8846 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
8847 require_noerr_quiet( err, exit );
8848
8849 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
8850 require_noerr_quiet( err, exit );
8851
8852 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
8853 require_noerr_quiet( err, exit );
8854
8855 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
8856 require_noerr_quiet( err, exit );
8857
8858 if( gMDNSReplier_Foreground )
8859 {
8860 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8861 }
8862
8863 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
8864 require_action( context, exit, err = kNoMemoryErr );
8865
8866 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
8867 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
8868 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
8869 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
8870 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
8871 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
8872 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
8873 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
8874 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
8875 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
8876
8877 #if( TARGET_OS_DARWIN )
8878 if( gMDNSReplier_FollowPID )
8879 {
8880 context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
8881 if( err || ( context->followPID < 0 ) )
8882 {
8883 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
8884 goto exit;
8885 }
8886
8887 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
8888 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
8889 require_noerr( err, exit );
8890 dispatch_resume( context->processMonitor );
8891 }
8892 else
8893 {
8894 context->followPID = -1;
8895 }
8896 #endif
8897
8898 if( context->maxDropCount > 0 )
8899 {
8900 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
8901 require_action( context->dropCounters, exit, err = kNoMemoryErr );
8902 }
8903
8904 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
8905 require_action( context->bitmaps, exit, err = kNoMemoryErr );
8906
8907 // Create the base hostname label.
8908
8909 len = strlen( gMDNSReplier_Hostname );
8910 if( context->maxInstanceCount > 1 )
8911 {
8912 unsigned int maxInstanceCount, digitCount;
8913
8914 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8915 // "-<instance index>" to the base hostname.
8916
8917 maxInstanceCount = context->maxInstanceCount;
8918 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
8919 len += ( 3 + digitCount );
8920 }
8921
8922 if( len <= kDomainLabelLengthMax )
8923 {
8924 uint8_t * dst = &name[ 1 ];
8925 uint8_t * lim = &name[ countof( name ) ];
8926
8927 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
8928 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8929
8930 err = DomainNameDupLower( name, &context->hostname, NULL );
8931 require_noerr( err, exit );
8932 }
8933 else
8934 {
8935 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8936 gMDNSReplier_Hostname, context->maxInstanceCount );
8937 goto exit;
8938 }
8939
8940 // Create the service label.
8941
8942 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
8943 if( len <= kDomainLabelLengthMax )
8944 {
8945 uint8_t * dst = &name[ 1 ];
8946 uint8_t * lim = &name[ countof( name ) ];
8947
8948 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
8949 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8950
8951 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
8952 require_noerr( err, exit );
8953 }
8954 else
8955 {
8956 FPrintF( stderr, "error: Service type tag is too long.\n" );
8957 goto exit;
8958 }
8959
8960 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
8961 require_noerr_quiet( err, exit );
8962
8963 ifname = if_indextoname( context->ifIndex, ifnameBuf );
8964 require_action( ifname, exit, err = kNameErr );
8965
8966 // Set up IPv4 socket.
8967
8968 if( context->useIPv4 )
8969 {
8970 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
8971 require_noerr( err, exit );
8972 }
8973
8974 // Set up IPv6 socket.
8975
8976 if( context->useIPv6 )
8977 {
8978 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
8979 require_noerr( err, exit );
8980 }
8981
8982 // Create dispatch read sources for socket(s).
8983
8984 if( IsValidSocket( sockV4 ) )
8985 {
8986 SocketContext * sockCtx;
8987
8988 err = SocketContextCreate( sockV4, context, &sockCtx );
8989 require_noerr( err, exit );
8990 sockV4 = kInvalidSocketRef;
8991
8992 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
8993 &context->readSourceV4 );
8994 if( err ) ForgetSocketContext( &sockCtx );
8995 require_noerr( err, exit );
8996
8997 dispatch_resume( context->readSourceV4 );
8998 }
8999
9000 if( IsValidSocket( sockV6 ) )
9001 {
9002 SocketContext * sockCtx;
9003
9004 err = SocketContextCreate( sockV6, context, &sockCtx );
9005 require_noerr( err, exit );
9006 sockV6 = kInvalidSocketRef;
9007
9008 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9009 &context->readSourceV6 );
9010 if( err ) ForgetSocketContext( &sockCtx );
9011 require_noerr( err, exit );
9012
9013 dispatch_resume( context->readSourceV6 );
9014 }
9015
9016 dispatch_main();
9017
9018 exit:
9019 ForgetSocket( &sockV4 );
9020 ForgetSocket( &sockV6 );
9021 exit( 1 );
9022 }
9023
9024 #if( TARGET_OS_DARWIN )
9025 //===========================================================================================================================
9026 // _MDNSReplierFollowedProcessHandler
9027 //===========================================================================================================================
9028
9029 static void _MDNSReplierFollowedProcessHandler( void *inContext )
9030 {
9031 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
9032
9033 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
9034 {
9035 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
9036 exit( 0 );
9037 }
9038 }
9039 #endif
9040
9041 //===========================================================================================================================
9042 // _MDNSReplierReadHandler
9043 //===========================================================================================================================
9044
9045 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9046
9047 static void _MDNSReplierReadHandler( void *inContext )
9048 {
9049 OSStatus err;
9050 SocketContext * const sockCtx = (SocketContext *) inContext;
9051 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
9052 size_t msgLen;
9053 sockaddr_ip sender;
9054 const DNSHeader * hdr;
9055 unsigned int flags, questionCount, i, j;
9056 const uint8_t * ptr;
9057 int drop, isMetaQuery;
9058
9059 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
9060 NULL, NULL, NULL, NULL );
9061 require_noerr( err, exit );
9062
9063 if( msgLen < kDNSHeaderLength )
9064 {
9065 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
9066 goto exit;
9067 }
9068
9069 // Perform header field checks.
9070 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9071
9072 hdr = (DNSHeader *) context->msgBuf;
9073 flags = DNSHeaderGetFlags( hdr );
9074 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
9075 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
9076 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
9077
9078 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
9079
9080 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9081 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
9082
9083 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9084
9085 questionCount = DNSHeaderGetQuestionCount( hdr );
9086 require_quiet( questionCount > 0, exit );
9087
9088 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
9089
9090 isMetaQuery = false;
9091 ptr = (const uint8_t *) &hdr[ 1 ];
9092 for( i = 0; i < questionCount; ++i )
9093 {
9094 unsigned int count, index;
9095 uint16_t qtype, qclass;
9096 uint8_t qname[ kDomainNameLengthMax ];
9097
9098 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
9099 require_noerr_quiet( err, exit );
9100
9101 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
9102
9103 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
9104 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
9105 {
9106 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
9107 {
9108 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
9109 }
9110 }
9111 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
9112 {
9113 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
9114 {
9115 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
9116 {
9117 if( count < 64 )
9118 {
9119 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
9120 break;
9121 }
9122 else
9123 {
9124 context->bitmaps[ j ] = ~UINT64_C( 0 );
9125 count -= 64;
9126 }
9127 }
9128 }
9129 }
9130 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
9131 {
9132 isMetaQuery = true;
9133 }
9134 }
9135
9136 // Attempt to answer the query message using selected record sets.
9137
9138 if( isMetaQuery )
9139 {
9140 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
9141 check_noerr( err );
9142 }
9143 if( drop ) goto exit;
9144
9145 for( i = 0; i < context->bitmapCount; ++i )
9146 {
9147 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
9148 {
9149 const uint64_t bitmask = UINT64_C( 1 ) << j;
9150
9151 if( context->bitmaps[ i ] & bitmask )
9152 {
9153 context->bitmaps[ i ] &= ~bitmask;
9154
9155 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
9156 ( i * 64 ) + j + 1 );
9157 check_noerr( err );
9158 }
9159 }
9160 }
9161
9162 exit:
9163 return;
9164 }
9165
9166 //===========================================================================================================================
9167 // _MDNSReplierAnswerQuery
9168 //===========================================================================================================================
9169
9170 static OSStatus
9171 _MDNSReplierAnswerQuery(
9172 MDNSReplierContext * inContext,
9173 const uint8_t * inQueryPtr,
9174 size_t inQueryLen,
9175 sockaddr_ip * inSender,
9176 SocketRef inSock,
9177 unsigned int inIndex )
9178 {
9179 OSStatus err;
9180 const DNSHeader * hdr;
9181 const uint8_t * ptr;
9182 unsigned int questionCount, answerCount, i;
9183 MRResourceRecord * ucastAnswerList = NULL;
9184 MRResourceRecord * mcastAnswerList = NULL;
9185
9186 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9187
9188 // Get answers for questions.
9189
9190 check( inQueryLen >= kDNSHeaderLength );
9191 hdr = (const DNSHeader *) inQueryPtr;
9192 questionCount = DNSHeaderGetQuestionCount( hdr );
9193
9194 ptr = (const uint8_t *) &hdr[ 1 ];
9195 for( i = 0; i < questionCount; ++i )
9196 {
9197 MRResourceRecord ** answerListPtr;
9198 uint16_t qtype, qclass;
9199 uint8_t qname[ kDomainNameLengthMax ];
9200
9201 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
9202 require_noerr_quiet( err, exit );
9203
9204 if( qclass & kQClassUnicastResponseBit )
9205 {
9206 qclass &= ~kQClassUnicastResponseBit;
9207 answerListPtr = &ucastAnswerList;
9208 }
9209 else
9210 {
9211 answerListPtr = &mcastAnswerList;
9212 }
9213
9214 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
9215 require_noerr( err, exit );
9216 }
9217 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9218
9219 // Suppress known answers.
9220 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9221 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9222
9223 answerCount = DNSHeaderGetAnswerCount( hdr );
9224 for( i = 0; i < answerCount; ++i )
9225 {
9226 const uint8_t * rdataPtr;
9227 const uint8_t * recordPtr;
9228 uint16_t type, class;
9229 uint8_t name[ kDomainNameLengthMax ];
9230 uint8_t instance[ kDomainNameLengthMax ];
9231
9232 recordPtr = ptr;
9233 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
9234 require_noerr_quiet( err, exit );
9235
9236 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
9237
9238 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
9239 require_noerr( err, exit );
9240
9241 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
9242 require_noerr_quiet( err, exit );
9243
9244 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
9245 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
9246 }
9247 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9248
9249 // Send or drop responses.
9250
9251 if( ucastAnswerList )
9252 {
9253 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
9254 require_noerr( err, exit );
9255 }
9256
9257 if( mcastAnswerList )
9258 {
9259 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
9260 require_noerr( err, exit );
9261 }
9262 err = kNoErr;
9263
9264 exit:
9265 _MRResourceRecordFreeList( ucastAnswerList );
9266 _MRResourceRecordFreeList( mcastAnswerList );
9267 return( err );
9268 }
9269
9270 //===========================================================================================================================
9271 // _MDNSReplierAnswerListAdd
9272 //===========================================================================================================================
9273
9274 static OSStatus
9275 _MDNSReplierAnswerListAdd(
9276 MDNSReplierContext * inContext,
9277 MRResourceRecord ** inAnswerList,
9278 unsigned int inIndex,
9279 const uint8_t * inName,
9280 unsigned int inType,
9281 unsigned int inClass )
9282 {
9283 OSStatus err;
9284 uint8_t * recordName = NULL;
9285 uint8_t * rdataPtr = NULL;
9286 size_t rdataLen;
9287 MRResourceRecord * answer;
9288 MRResourceRecord ** answerPtr;
9289 const uint8_t * const hostname = inContext->hostname;
9290 unsigned int i;
9291 uint32_t index;
9292 unsigned int count, txtSize;
9293
9294 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9295 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
9296
9297 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9298 {
9299 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
9300 {
9301 err = kNoErr;
9302 goto exit;
9303 }
9304 }
9305
9306 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9307 // instance count are for answering queries about service instances.
9308
9309 if( inIndex == 0 )
9310 {
9311 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
9312 {
9313 int listHasTXT = false;
9314
9315 if( inType == kDNSServiceType_ANY )
9316 {
9317 for( answer = *inAnswerList; answer; answer = answer->next )
9318 {
9319 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
9320 {
9321 listHasTXT = true;
9322 break;
9323 }
9324 }
9325 }
9326
9327 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9328 {
9329 err = DomainNameDupLower( inName, &recordName, NULL );
9330 require_noerr( err, exit );
9331
9332 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
9333 require_noerr( err, exit );
9334
9335 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9336 (uint16_t) rdataLen, rdataPtr, &answer );
9337 require_noerr( err, exit );
9338 recordName = NULL;
9339 rdataPtr = NULL;
9340
9341 *answerPtr = answer;
9342 }
9343 else if( inType == kDNSServiceType_NSEC )
9344 {
9345 err = DomainNameDupLower( inName, &recordName, NULL );
9346 require_noerr( err, exit );
9347
9348 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
9349 require_noerr( err, exit );
9350
9351 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9352 (uint16_t) rdataLen, rdataPtr, &answer );
9353 require_noerr( err, exit );
9354 recordName = NULL;
9355 rdataPtr = NULL;
9356
9357 *answerPtr = answer;
9358 }
9359 }
9360 }
9361 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
9362 {
9363 int listHasA = false;
9364 int listHasAAAA = false;
9365
9366 if( inType == kDNSServiceType_ANY )
9367 {
9368 for( answer = *inAnswerList; answer; answer = answer->next )
9369 {
9370 if( answer->type == kDNSServiceType_A )
9371 {
9372 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
9373 }
9374 else if( answer->type == kDNSServiceType_AAAA )
9375 {
9376 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
9377 }
9378 if( listHasA && listHasAAAA ) break;
9379 }
9380 }
9381
9382 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
9383 {
9384 for( i = 1; i <= inContext->recordCountA; ++i )
9385 {
9386 err = DomainNameDupLower( inName, &recordName, NULL );
9387 require_noerr( err, exit );
9388
9389 rdataLen = 4;
9390 rdataPtr = (uint8_t *) malloc( rdataLen );
9391 require_action( rdataPtr, exit, err = kNoMemoryErr );
9392
9393 rdataPtr[ 0 ] = 0;
9394 WriteBig16( &rdataPtr[ 1 ], inIndex );
9395 rdataPtr[ 3 ] = (uint8_t) i;
9396
9397 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9398 (uint16_t) rdataLen, rdataPtr, &answer );
9399 require_noerr( err, exit );
9400 recordName = NULL;
9401 rdataPtr = NULL;
9402
9403 *answerPtr = answer;
9404 answerPtr = &answer->next;
9405 }
9406 }
9407
9408 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
9409 {
9410 for( i = 1; i <= inContext->recordCountAAAA; ++i )
9411 {
9412 err = DomainNameDupLower( inName, &recordName, NULL );
9413 require_noerr( err, exit );
9414
9415 rdataLen = 16;
9416 rdataPtr = (uint8_t *) _memdup( kMDNSReplierBaseAddrV6, rdataLen );
9417 require_action( rdataPtr, exit, err = kNoMemoryErr );
9418
9419 WriteBig16( &rdataPtr[ 12 ], inIndex );
9420 rdataPtr[ 15 ] = (uint8_t) i;
9421
9422 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9423 (uint16_t) rdataLen, rdataPtr, &answer );
9424 require_noerr( err, exit );
9425 recordName = NULL;
9426 rdataPtr = NULL;
9427
9428 *answerPtr = answer;
9429 answerPtr = &answer->next;
9430 }
9431 }
9432 else if( inType == kDNSServiceType_NSEC )
9433 {
9434 err = DomainNameDupLower( inName, &recordName, NULL );
9435 require_noerr( err, exit );
9436
9437 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
9438 {
9439 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
9440 require_noerr( err, exit );
9441 }
9442 else if( inContext->recordCountA > 0 )
9443 {
9444 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
9445 require_noerr( err, exit );
9446 }
9447 else if( inContext->recordCountAAAA > 0 )
9448 {
9449 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
9450 require_noerr( err, exit );
9451 }
9452 else
9453 {
9454 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
9455 require_noerr( err, exit );
9456 }
9457
9458 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9459 (uint16_t) rdataLen, rdataPtr, &answer );
9460 require_noerr( err, exit );
9461 recordName = NULL;
9462 rdataPtr = NULL;
9463
9464 *answerPtr = answer;
9465 }
9466 }
9467 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
9468 {
9469 int listHasPTR = false;
9470
9471 if( inType == kDNSServiceType_ANY )
9472 {
9473 for( answer = *inAnswerList; answer; answer = answer->next )
9474 {
9475 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
9476 {
9477 listHasPTR = true;
9478 break;
9479 }
9480 }
9481 }
9482
9483 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
9484 {
9485 size_t recordNameLen;
9486 uint8_t * ptr;
9487 uint8_t * lim;
9488
9489 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
9490 require_noerr( err, exit );
9491
9492 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
9493 rdataPtr = (uint8_t *) malloc( rdataLen );
9494 require_action( rdataPtr, exit, err = kNoMemoryErr );
9495
9496 lim = &rdataPtr[ rdataLen ];
9497
9498 ptr = &rdataPtr[ 1 ];
9499 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9500 ptr += hostname[ 0 ];
9501 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
9502 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
9503
9504 check( (size_t)( lim - ptr ) >= recordNameLen );
9505 memcpy( ptr, recordName, recordNameLen );
9506 ptr += recordNameLen;
9507
9508 rdataLen = (size_t)( ptr - rdataPtr );
9509
9510 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9511 (uint16_t) rdataLen, rdataPtr, &answer );
9512 require_noerr( err, exit );
9513 recordName = NULL;
9514 rdataPtr = NULL;
9515
9516 *answerPtr = answer;
9517 }
9518 }
9519 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
9520 ( index == inIndex ) && ( count >= inIndex ) )
9521 {
9522 int listHasSRV = false;
9523 int listHasTXT = false;
9524
9525 if( inType == kDNSServiceType_ANY )
9526 {
9527 for( answer = *inAnswerList; answer; answer = answer->next )
9528 {
9529 if( answer->type == kDNSServiceType_SRV )
9530 {
9531 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
9532 }
9533 else if( answer->type == kDNSServiceType_TXT )
9534 {
9535 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
9536 }
9537 if( listHasSRV && listHasTXT ) break;
9538 }
9539 }
9540
9541 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
9542 {
9543 dns_fixed_fields_srv * fields;
9544 uint8_t * ptr;
9545 uint8_t * lim;
9546 uint8_t * targetPtr;
9547
9548 err = DomainNameDupLower( inName, &recordName, NULL );
9549 require_noerr( err, exit );
9550
9551 rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
9552 rdataPtr = (uint8_t *) malloc( rdataLen );
9553 require_action( rdataPtr, exit, err = kNoMemoryErr );
9554
9555 lim = &rdataPtr[ rdataLen ];
9556
9557 fields = (dns_fixed_fields_srv *) rdataPtr;
9558 dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
9559
9560 targetPtr = (uint8_t *) &fields[ 1 ];
9561
9562 ptr = &targetPtr[ 1 ];
9563 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9564 ptr += hostname[ 0 ];
9565 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
9566 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
9567
9568 check( (size_t)( lim - ptr ) >= kLocalNameLen );
9569 memcpy( ptr, kLocalName, kLocalNameLen );
9570 ptr += kLocalNameLen;
9571
9572 rdataLen = (size_t)( ptr - rdataPtr );
9573
9574 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9575 (uint16_t) rdataLen, rdataPtr, &answer );
9576 require_noerr( err, exit );
9577 recordName = NULL;
9578 rdataPtr = NULL;
9579
9580 *answerPtr = answer;
9581 answerPtr = &answer->next;
9582 }
9583
9584 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9585 {
9586 err = DomainNameDupLower( inName, &recordName, NULL );
9587 require_noerr( err, exit );
9588
9589 rdataLen = txtSize;
9590 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
9591 require_noerr( err, exit );
9592
9593 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9594 (uint16_t) rdataLen, rdataPtr, &answer );
9595 require_noerr( err, exit );
9596 recordName = NULL;
9597 rdataPtr = NULL;
9598
9599 *answerPtr = answer;
9600 }
9601 else if( inType == kDNSServiceType_NSEC )
9602 {
9603 err = DomainNameDupLower( inName, &recordName, NULL );
9604 require_noerr( err, exit );
9605
9606 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
9607 require_noerr( err, exit );
9608
9609 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9610 (uint16_t) rdataLen, rdataPtr, &answer );
9611 require_noerr( err, exit );
9612 recordName = NULL;
9613 rdataPtr = NULL;
9614
9615 *answerPtr = answer;
9616 }
9617 }
9618 err = kNoErr;
9619
9620 exit:
9621 FreeNullSafe( recordName );
9622 FreeNullSafe( rdataPtr );
9623 return( err );
9624 }
9625
9626 //===========================================================================================================================
9627 // _MDNSReplierAnswerListRemovePTR
9628 //===========================================================================================================================
9629
9630 static void
9631 _MDNSReplierAnswerListRemovePTR(
9632 MRResourceRecord ** inAnswerListPtr,
9633 const uint8_t * inName,
9634 const uint8_t * inRData )
9635 {
9636 MRResourceRecord * answer;
9637 MRResourceRecord ** answerPtr;
9638
9639 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9640 {
9641 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
9642 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
9643 }
9644 if( answer )
9645 {
9646 *answerPtr = answer->next;
9647 _MRResourceRecordFree( answer );
9648 }
9649 }
9650
9651 //===========================================================================================================================
9652 // _MDNSReplierSendOrDropResponse
9653 //===========================================================================================================================
9654
9655 static OSStatus
9656 _MDNSReplierSendOrDropResponse(
9657 MDNSReplierContext * inContext,
9658 MRResourceRecord * inAnswerList,
9659 sockaddr_ip * inQuerier,
9660 SocketRef inSock,
9661 unsigned int inIndex,
9662 Boolean inUnicast )
9663 {
9664 OSStatus err;
9665 uint8_t * responsePtr = NULL;
9666 size_t responseLen;
9667 const struct sockaddr * destAddr;
9668 ssize_t n;
9669 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
9670 int drop;
9671
9672 check( inIndex <= inContext->maxInstanceCount );
9673
9674 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9675 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9676 // as opposed to those for service instance records, are never dropped.
9677
9678 drop = false;
9679 if( inIndex > 0 )
9680 {
9681 if( inContext->maxDropCount > 0 )
9682 {
9683 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
9684
9685 if( *dropCount < inContext->maxDropCount )
9686 {
9687 if( ShouldDrop( dropRate ) ) drop = true;
9688 *dropCount += 1;
9689 }
9690 }
9691 else if( ShouldDrop( dropRate ) )
9692 {
9693 drop = true;
9694 }
9695 }
9696
9697 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
9698 require_noerr( err, exit );
9699
9700 if( inUnicast )
9701 {
9702 destAddr = &inQuerier->sa;
9703 }
9704 else
9705 {
9706 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9707 }
9708
9709 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9710 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
9711
9712 if( !drop )
9713 {
9714 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
9715 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
9716 require_noerr( err, exit );
9717 }
9718
9719 exit:
9720 FreeNullSafe( responsePtr );
9721 return( err );
9722 }
9723
9724 //===========================================================================================================================
9725 // _MDNSReplierCreateResponse
9726 //===========================================================================================================================
9727
9728 static OSStatus
9729 _MDNSReplierCreateResponse(
9730 MDNSReplierContext * inContext,
9731 MRResourceRecord * inAnswerList,
9732 unsigned int inIndex,
9733 uint8_t ** outResponsePtr,
9734 size_t * outResponseLen )
9735 {
9736 OSStatus err;
9737 DataBuffer responseDB;
9738 DNSHeader hdr;
9739 MRResourceRecord * answer;
9740 uint8_t * responsePtr;
9741 size_t responseLen, len;
9742 unsigned int answerCount, recordCount;
9743 MRNameOffsetItem * nameOffsetList = NULL;
9744
9745 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
9746
9747 // The current answers in the answer list will make up the response's Answer Record Section.
9748
9749 answerCount = 0;
9750 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
9751
9752 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9753
9754 if( !inContext->noAdditionals )
9755 {
9756 for( answer = inAnswerList; answer; answer = answer->next )
9757 {
9758 switch( answer->type )
9759 {
9760 case kDNSServiceType_PTR:
9761 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
9762 answer->class );
9763 require_noerr( err, exit );
9764
9765 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
9766 answer->class );
9767 require_noerr( err, exit );
9768 break;
9769
9770 case kDNSServiceType_SRV:
9771 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
9772 answer->class );
9773 require_noerr( err, exit );
9774
9775 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
9776 answer->class );
9777 require_noerr( err, exit );
9778
9779 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9780 answer->class );
9781 require_noerr( err, exit );
9782 break;
9783
9784 case kDNSServiceType_TXT:
9785 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9786 answer->class );
9787 require_noerr( err, exit );
9788 break;
9789
9790 case kDNSServiceType_A:
9791 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
9792 answer->class );
9793 require_noerr( err, exit );
9794
9795 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9796 answer->class );
9797 require_noerr( err, exit );
9798 break;
9799
9800 case kDNSServiceType_AAAA:
9801 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
9802 answer->class );
9803 require_noerr( err, exit );
9804
9805 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9806 answer->class );
9807 require_noerr( err, exit );
9808 break;
9809
9810 default:
9811 break;
9812 }
9813 }
9814 }
9815
9816 // Append a provisional header to the response message.
9817
9818 memset( &hdr, 0, sizeof( hdr ) );
9819 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
9820
9821 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
9822 require_noerr( err, exit );
9823
9824 // Append answers to response message.
9825
9826 responseLen = DataBuffer_GetLen( &responseDB );
9827 recordCount = 0;
9828 for( answer = inAnswerList; answer; answer = answer->next )
9829 {
9830 dns_fixed_fields_record fields;
9831 unsigned int class;
9832
9833 // Append record NAME.
9834
9835 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
9836 require_noerr( err, exit );
9837
9838 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9839
9840 class = answer->class;
9841 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
9842 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
9843 ( answer->type == kDNSServiceType_NSEC ) )
9844 {
9845 class |= kRRClassCacheFlushBit;
9846 }
9847
9848 dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
9849 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
9850 require_noerr( err, exit );
9851
9852 // Append record RDATA.
9853 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9854
9855 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
9856 ( answer->type == kDNSServiceType_NSEC ) )
9857 {
9858 size_t rdlength;
9859 uint8_t * rdLengthPtr;
9860 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
9861 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
9862
9863 if( answer->type == kDNSServiceType_PTR )
9864 {
9865 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9866 require_noerr( err, exit );
9867 }
9868 else if( answer->type == kDNSServiceType_SRV )
9869 {
9870 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
9871
9872 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
9873 require_noerr( err, exit );
9874
9875 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
9876 require_noerr( err, exit );
9877 }
9878 else
9879 {
9880 const size_t nameLen = DomainNameLength( answer->rdata );
9881
9882 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9883 require_noerr( err, exit );
9884
9885 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
9886
9887 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
9888 require_noerr( err, exit );
9889 }
9890
9891 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9892
9893 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
9894 check( rdlength <= UINT16_MAX );
9895
9896 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
9897 WriteBig16( rdLengthPtr, rdlength );
9898 }
9899 else
9900 {
9901 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
9902 require_noerr( err, exit );
9903 }
9904
9905 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
9906 responseLen = DataBuffer_GetLen( &responseDB );
9907 ++recordCount;
9908 }
9909
9910 // Set the response header's Answer and Additional record counts.
9911 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9912 // response message to exceed the maximum mDNS message size.
9913
9914 if( recordCount <= answerCount )
9915 {
9916 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
9917 }
9918 else
9919 {
9920 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
9921 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
9922 }
9923
9924 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
9925 require_noerr( err, exit );
9926
9927 if( outResponsePtr ) *outResponsePtr = responsePtr;
9928 if( outResponseLen ) *outResponseLen = responseLen;
9929
9930 exit:
9931 _MRNameOffsetItemFreeList( nameOffsetList );
9932 DataBuffer_Free( &responseDB );
9933 return( err );
9934 }
9935
9936 //===========================================================================================================================
9937 // _MDNSReplierAppendNameToResponse
9938 //===========================================================================================================================
9939
9940 static OSStatus
9941 _MDNSReplierAppendNameToResponse(
9942 DataBuffer * inResponse,
9943 const uint8_t * inName,
9944 MRNameOffsetItem ** inNameOffsetListPtr )
9945 {
9946 OSStatus err;
9947 const uint8_t * subname;
9948 const uint8_t * limit;
9949 size_t nameOffset;
9950 MRNameOffsetItem * item;
9951 uint8_t compressionPtr[ 2 ];
9952
9953 nameOffset = DataBuffer_GetLen( inResponse );
9954
9955 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9956
9957 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
9958 {
9959 for( item = *inNameOffsetListPtr; item; item = item->next )
9960 {
9961 if( DomainNameEqual( item->name, subname ) ) break;
9962 }
9963
9964 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9965 // the subname's first label.
9966
9967 if( item )
9968 {
9969 DNSMessageWriteLabelPointer( compressionPtr, item->offset );
9970
9971 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
9972 require_noerr( err, exit );
9973 break;
9974 }
9975 else
9976 {
9977 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
9978 require_noerr( err, exit );
9979 }
9980 }
9981
9982 // 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
9983 // label were appended to the response message, so a root label is needed to terminate the complete name.
9984
9985 if( subname[ 0 ] == 0 )
9986 {
9987 err = DataBuffer_Append( inResponse, "", 1 );
9988 require_noerr( err, exit );
9989 }
9990
9991 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
9992
9993 limit = subname;
9994 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
9995 {
9996 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
9997
9998 if( subnameOffset > kDNSCompressionOffsetMax ) break;
9999
10000 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
10001 require_noerr( err, exit );
10002
10003 item->next = *inNameOffsetListPtr;
10004 *inNameOffsetListPtr = item;
10005 }
10006 err = kNoErr;
10007
10008 exit:
10009 return( err );
10010 }
10011
10012 //===========================================================================================================================
10013 // _MDNSReplierServiceTypeMatch
10014 //===========================================================================================================================
10015
10016 static Boolean
10017 _MDNSReplierServiceTypeMatch(
10018 const MDNSReplierContext * inContext,
10019 const uint8_t * inName,
10020 unsigned int * outTXTSize,
10021 unsigned int * outCount )
10022 {
10023 OSStatus err;
10024 const char * ptr;
10025 const char * end;
10026 uint32_t txtSize, count;
10027 const uint8_t * const serviceLabel = inContext->serviceLabel;
10028 int nameMatches = false;
10029
10030 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
10031 if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
10032
10033 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
10034 end = (const char *) &inName[ 1 + inName[ 0 ] ];
10035
10036 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10037 ++ptr;
10038
10039 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
10040 require_noerr_quiet( err, exit );
10041 require_quiet( txtSize <= UINT16_MAX, exit );
10042
10043 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10044 ++ptr;
10045
10046 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
10047 require_noerr_quiet( err, exit );
10048 require_quiet( count <= UINT16_MAX, exit );
10049 require_quiet( ptr == end, exit );
10050
10051 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
10052 nameMatches = true;
10053
10054 if( outTXTSize ) *outTXTSize = txtSize;
10055 if( outCount ) *outCount = count;
10056
10057 exit:
10058 return( nameMatches ? true : false );
10059 }
10060
10061 //===========================================================================================================================
10062 // _MDNSReplierServiceInstanceNameMatch
10063 //===========================================================================================================================
10064
10065 static Boolean
10066 _MDNSReplierServiceInstanceNameMatch(
10067 const MDNSReplierContext * inContext,
10068 const uint8_t * inName,
10069 unsigned int * outIndex,
10070 unsigned int * outTXTSize,
10071 unsigned int * outCount )
10072 {
10073 OSStatus err;
10074 const uint8_t * ptr;
10075 const uint8_t * end;
10076 uint32_t index;
10077 unsigned int txtSize, count;
10078 const uint8_t * const hostname = inContext->hostname;
10079 int nameMatches = false;
10080
10081 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10082 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10083
10084 ptr = &inName[ 1 + hostname[ 0 ] ];
10085 end = &inName[ 1 + inName[ 0 ] ];
10086 if( ptr < end )
10087 {
10088 require_quiet( ( end - ptr ) >= 2, exit );
10089 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
10090 ptr += 2;
10091
10092 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10093 require_noerr_quiet( err, exit );
10094 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10095
10096 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
10097 ++ptr;
10098 }
10099 else
10100 {
10101 index = 1;
10102 }
10103
10104 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
10105 nameMatches = true;
10106
10107 if( outIndex ) *outIndex = index;
10108 if( outTXTSize ) *outTXTSize = txtSize;
10109 if( outCount ) *outCount = count;
10110
10111 exit:
10112 return( nameMatches ? true : false );
10113 }
10114
10115 //===========================================================================================================================
10116 // _MDNSReplierAboutRecordNameMatch
10117 //===========================================================================================================================
10118
10119 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10120 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10121
10122 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
10123 {
10124 const uint8_t * subname;
10125 const uint8_t * const hostname = inContext->hostname;
10126 int nameMatches = false;
10127
10128 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
10129 subname = DomainNameGetNextLabel( inName );
10130
10131 if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
10132 subname = DomainNameGetNextLabel( subname );
10133
10134 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
10135 nameMatches = true;
10136
10137 exit:
10138 return( nameMatches ? true : false );
10139 }
10140
10141 //===========================================================================================================================
10142 // _MDNSReplierHostnameMatch
10143 //===========================================================================================================================
10144
10145 static Boolean
10146 _MDNSReplierHostnameMatch(
10147 const MDNSReplierContext * inContext,
10148 const uint8_t * inName,
10149 unsigned int * outIndex )
10150 {
10151 OSStatus err;
10152 const uint8_t * ptr;
10153 const uint8_t * end;
10154 uint32_t index;
10155 const uint8_t * const hostname = inContext->hostname;
10156 int nameMatches = false;
10157
10158 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10159 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10160
10161 ptr = &inName[ 1 + hostname[ 0 ] ];
10162 end = &inName[ 1 + inName[ 0 ] ];
10163 if( ptr < end )
10164 {
10165 require_quiet( *ptr == '-', exit );
10166 ++ptr;
10167
10168 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10169 require_noerr_quiet( err, exit );
10170 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10171 require_quiet( ptr == end, exit );
10172 }
10173 else
10174 {
10175 index = 1;
10176 }
10177
10178 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
10179 nameMatches = true;
10180
10181 if( outIndex ) *outIndex = index;
10182
10183 exit:
10184 return( nameMatches ? true : false );
10185 }
10186
10187 //===========================================================================================================================
10188 // _MDNSReplierCreateTXTRecord
10189 //===========================================================================================================================
10190
10191 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
10192 {
10193 OSStatus err;
10194 uint8_t * txt;
10195 uint8_t * ptr;
10196 size_t i, wholeCount, remCount;
10197 uint32_t hash;
10198 int n;
10199 uint8_t txtStr[ 16 ];
10200
10201 require_action_quiet( inSize > 0, exit, err = kSizeErr );
10202
10203 txt = (uint8_t *) malloc( inSize );
10204 require_action( txt, exit, err = kNoMemoryErr );
10205
10206 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
10207
10208 txtStr[ 0 ] = 15;
10209 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
10210 check( n == 15 );
10211
10212 ptr = txt;
10213 wholeCount = inSize / 16;
10214 for( i = 0; i < wholeCount; ++i )
10215 {
10216 memcpy( ptr, txtStr, 16 );
10217 ptr += 16;
10218 }
10219
10220 remCount = inSize % 16;
10221 if( remCount > 0 )
10222 {
10223 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
10224 memcpy( ptr, txtStr, remCount );
10225 ptr += remCount;
10226 }
10227 check( ptr == &txt[ inSize ] );
10228
10229 *outTXT = txt;
10230 err = kNoErr;
10231
10232 exit:
10233 return( err );
10234 }
10235
10236 //===========================================================================================================================
10237 // _MRResourceRecordCreate
10238 //===========================================================================================================================
10239
10240 static OSStatus
10241 _MRResourceRecordCreate(
10242 uint8_t * inName,
10243 uint16_t inType,
10244 uint16_t inClass,
10245 uint32_t inTTL,
10246 uint16_t inRDLength,
10247 uint8_t * inRData,
10248 MRResourceRecord ** outRecord )
10249 {
10250 OSStatus err;
10251 MRResourceRecord * obj;
10252
10253 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
10254 require_action( obj, exit, err = kNoMemoryErr );
10255
10256 obj->name = inName;
10257 obj->type = inType;
10258 obj->class = inClass;
10259 obj->ttl = inTTL;
10260 obj->rdlength = inRDLength;
10261 obj->rdata = inRData;
10262
10263 if( inType == kDNSServiceType_SRV )
10264 {
10265 require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
10266 obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
10267 }
10268
10269 *outRecord = obj;
10270 obj = NULL;
10271 err = kNoErr;
10272
10273 exit:
10274 FreeNullSafe( obj );
10275 return( err );
10276 }
10277
10278 //===========================================================================================================================
10279 // _MRResourceRecordFree
10280 //===========================================================================================================================
10281
10282 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
10283 {
10284 ForgetMem( &inRecord->name );
10285 ForgetMem( &inRecord->rdata );
10286 free( inRecord );
10287 }
10288
10289 //===========================================================================================================================
10290 // _MRResourceRecordFreeList
10291 //===========================================================================================================================
10292
10293 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
10294 {
10295 MRResourceRecord * record;
10296
10297 while( ( record = inList ) != NULL )
10298 {
10299 inList = record->next;
10300 _MRResourceRecordFree( record );
10301 }
10302 }
10303
10304 //===========================================================================================================================
10305 // _MRNameOffsetItemCreate
10306 //===========================================================================================================================
10307
10308 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
10309 {
10310 OSStatus err;
10311 MRNameOffsetItem * obj;
10312 size_t nameLen;
10313
10314 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
10315
10316 nameLen = DomainNameLength( inName );
10317 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
10318 require_action( obj, exit, err = kNoMemoryErr );
10319
10320 obj->offset = inOffset;
10321 memcpy( obj->name, inName, nameLen );
10322
10323 *outItem = obj;
10324 err = kNoErr;
10325
10326 exit:
10327 return( err );
10328 }
10329
10330 //===========================================================================================================================
10331 // _MRNameOffsetItemFree
10332 //===========================================================================================================================
10333
10334 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
10335 {
10336 free( inItem );
10337 }
10338
10339 //===========================================================================================================================
10340 // _MRNameOffsetItemFreeList
10341 //===========================================================================================================================
10342
10343 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
10344 {
10345 MRNameOffsetItem * item;
10346
10347 while( ( item = inList ) != NULL )
10348 {
10349 inList = item->next;
10350 _MRNameOffsetItemFree( item );
10351 }
10352 }
10353
10354 //===========================================================================================================================
10355 // GAIPerfCmd
10356 //===========================================================================================================================
10357
10358 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10359
10360 typedef struct GAITesterPrivate * GAITesterRef;
10361 typedef struct GAITestCase GAITestCase;
10362
10363 typedef struct
10364 {
10365 const char * name; // Domain name that was resolved.
10366 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
10367 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
10368 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
10369 OSStatus error;
10370
10371 } GAITestItemResult;
10372
10373 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
10374 typedef void
10375 ( *GAITesterResultsHandler_f )(
10376 const char * inCaseTitle,
10377 NanoTime64 inCaseStartTime,
10378 NanoTime64 inCaseEndTime,
10379 const GAITestItemResult * inResultArray,
10380 size_t inResultCount,
10381 void * inContext );
10382
10383 typedef unsigned int GAITestAddrType;
10384 #define kGAITestAddrType_None 0
10385 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10386 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10387 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10388
10389 #define GAITestAddrTypeIsValid( X ) \
10390 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10391
10392 typedef struct
10393 {
10394 GAITesterRef tester; // GAI tester object.
10395 CFMutableArrayRef testCaseResults; // Array of test case results.
10396 unsigned int iterTimeLimitMs; // Amount of time to allow each iteration to complete.
10397 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10398 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
10399 unsigned int defaultIterCount; // Default test case iteration count.
10400 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
10401 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
10402 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
10403 OutputFormatType outputFormat; // Format of test results output.
10404 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10405 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
10406 Boolean testFailed; // True if at least one test case iteration failed.
10407
10408 } GAIPerfContext;
10409
10410 static void GAIPerfContextFree( GAIPerfContext *inContext );
10411 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
10412 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
10413 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
10414 static void
10415 GAIPerfResultsHandler(
10416 const char * inCaseTitle,
10417 NanoTime64 inCaseStartTime,
10418 NanoTime64 inCaseEndTime,
10419 const GAITestItemResult * inResultArray,
10420 size_t inResultCount,
10421 void * inContext );
10422 static void GAIPerfSignalHandler( void *inContext );
10423
10424 CFTypeID GAITesterGetTypeID( void );
10425 static OSStatus
10426 GAITesterCreate(
10427 dispatch_queue_t inQueue,
10428 unsigned int inCallDelayMs,
10429 int inServerDelayMs,
10430 int inServerDefaultTTL,
10431 Boolean inSkipPathEvaluation,
10432 Boolean inBadUDPMode,
10433 GAITesterRef * outTester );
10434 static void GAITesterStart( GAITesterRef inTester );
10435 static void GAITesterStop( GAITesterRef inTester );
10436 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
10437 static void
10438 GAITesterSetStopHandler(
10439 GAITesterRef inTester,
10440 GAITesterStopHandler_f inEventHandler,
10441 void * inEventContext );
10442 static void
10443 GAITesterSetResultsHandler(
10444 GAITesterRef inTester,
10445 GAITesterResultsHandler_f inResultsHandler,
10446 void * inResultsContext );
10447
10448 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
10449 static void GAITestCaseFree( GAITestCase *inCase );
10450 static OSStatus
10451 GAITestCaseAddItem(
10452 GAITestCase * inCase,
10453 unsigned int inAliasCount,
10454 unsigned int inAddressCount,
10455 int inTTL,
10456 GAITestAddrType inHasAddrs,
10457 GAITestAddrType inWantAddrs,
10458 unsigned int inTimeLimitMs,
10459 unsigned int inItemCount );
10460 static OSStatus
10461 GAITestCaseAddLocalHostItem(
10462 GAITestCase * inCase,
10463 GAITestAddrType inWantAddrs,
10464 unsigned int inTimeLimitMs,
10465 unsigned int inItemCount );
10466
10467 static void GAIPerfCmd( void )
10468 {
10469 OSStatus err;
10470 GAIPerfContext * context = NULL;
10471
10472 err = CheckRootUser();
10473 require_noerr_quiet( err, exit );
10474
10475 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
10476 require_noerr_quiet( err, exit );
10477
10478 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
10479 require_noerr_quiet( err, exit );
10480
10481 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
10482 require_noerr_quiet( err, exit );
10483
10484 err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
10485 require_noerr_quiet( err, exit );
10486
10487 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
10488 require_action( context, exit, err = kNoMemoryErr );
10489
10490 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
10491 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
10492
10493 context->iterTimeLimitMs = (unsigned int) gGAIPerf_IterationTimeLimitMs;
10494 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
10495 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
10496 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
10497 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
10498 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
10499
10500 if( gGAIPerf_OutputFilePath )
10501 {
10502 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
10503 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
10504 }
10505
10506 err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
10507 require_noerr_quiet( err, exit );
10508
10509 err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
10510 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
10511 require_noerr( err, exit );
10512
10513 check( gGAIPerf_TestSuite );
10514 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
10515 {
10516 err = GAIPerfAddBasicTestCases( context );
10517 require_noerr( err, exit );
10518 }
10519 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
10520 {
10521 err = GAIPerfAddAdvancedTestCases( context );
10522 require_noerr( err, exit );
10523 }
10524 else
10525 {
10526 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
10527 goto exit;
10528 }
10529
10530 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
10531 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
10532
10533 signal( SIGINT, SIG_IGN );
10534 err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource );
10535 require_noerr( err, exit );
10536 dispatch_resume( context->sigIntSource );
10537
10538 signal( SIGTERM, SIG_IGN );
10539 err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource );
10540 require_noerr( err, exit );
10541 dispatch_resume( context->sigTermSource );
10542
10543 GAITesterStart( context->tester );
10544 dispatch_main();
10545
10546 exit:
10547 if( context ) GAIPerfContextFree( context );
10548 exit( 1 );
10549 }
10550
10551 //===========================================================================================================================
10552 // GAIPerfContextFree
10553 //===========================================================================================================================
10554
10555 static void GAIPerfContextFree( GAIPerfContext *inContext )
10556 {
10557 ForgetCF( &inContext->tester );
10558 ForgetCF( &inContext->testCaseResults );
10559 ForgetMem( &inContext->outputFilePath );
10560 dispatch_source_forget( &inContext->sigIntSource );
10561 dispatch_source_forget( &inContext->sigTermSource );
10562 free( inContext );
10563 }
10564
10565 //===========================================================================================================================
10566 // GAIPerfAddAdvancedTestCases
10567 //===========================================================================================================================
10568
10569 #define kTestCaseTitleBufferSize 128
10570
10571 static void
10572 _GAIPerfWriteTestCaseTitle(
10573 char inBuffer[ kTestCaseTitleBufferSize ],
10574 unsigned int inCNAMERecordCount,
10575 unsigned int inARecordCount,
10576 unsigned int inAAAARecordCount,
10577 GAITestAddrType inRequested,
10578 unsigned int inIterationCount,
10579 Boolean inIterationsAreUnique );
10580 static void
10581 _GAIPerfWriteLocalHostTestCaseTitle(
10582 char inBuffer[ kTestCaseTitleBufferSize ],
10583 GAITestAddrType inRequested,
10584 unsigned int inIterationCount );
10585
10586 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10587 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10588
10589 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
10590 {
10591 OSStatus err;
10592 unsigned int aliasCount, addressCount, i;
10593 GAITestCase * testCase = NULL;
10594 char title[ kTestCaseTitleBufferSize ];
10595
10596 aliasCount = 0;
10597 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
10598 {
10599 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
10600 {
10601 // Add a test case to resolve a domain name with
10602 //
10603 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10604 //
10605 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10606 // requires server queries.
10607
10608 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10609 inContext->defaultIterCount, true );
10610
10611 err = GAITestCaseCreate( title, &testCase );
10612 require_noerr( err, exit );
10613
10614 for( i = 0; i < inContext->defaultIterCount; ++i )
10615 {
10616 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10617 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10618 require_noerr( err, exit );
10619 }
10620
10621 err = GAITesterAddTestCase( inContext->tester, testCase );
10622 require_noerr( err, exit );
10623 testCase = NULL;
10624
10625 // Add a test case to resolve a domain name with
10626 //
10627 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10628 //
10629 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10630 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10631 // ideally require no server queries, i.e., the results should come from the cache.
10632
10633 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10634 inContext->defaultIterCount, false );
10635
10636 err = GAITestCaseCreate( title, &testCase );
10637 require_noerr( err, exit );
10638
10639 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10640 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
10641 require_noerr( err, exit );
10642
10643 err = GAITesterAddTestCase( inContext->tester, testCase );
10644 require_noerr( err, exit );
10645 testCase = NULL;
10646 }
10647
10648 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
10649 }
10650
10651 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10652
10653 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10654
10655 err = GAITestCaseCreate( title, &testCase );
10656 require_noerr( err, exit );
10657
10658 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10659 inContext->defaultIterCount );
10660 require_noerr( err, exit );
10661
10662 err = GAITesterAddTestCase( inContext->tester, testCase );
10663 require_noerr( err, exit );
10664 testCase = NULL;
10665
10666 exit:
10667 if( testCase ) GAITestCaseFree( testCase );
10668 return( err );
10669 }
10670
10671 //===========================================================================================================================
10672 // _GAIPerfWriteTestCaseTitle
10673 //===========================================================================================================================
10674
10675 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10676 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10677 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10678 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10679 "" )
10680
10681 static void
10682 _GAIPerfWriteTestCaseTitle(
10683 char inBuffer[ kTestCaseTitleBufferSize ],
10684 unsigned int inCNAMERecordCount,
10685 unsigned int inARecordCount,
10686 unsigned int inAAAARecordCount,
10687 GAITestAddrType inRequested,
10688 unsigned int inIterationCount,
10689 Boolean inIterationsAreUnique )
10690 {
10691 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10692 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
10693 inIterationCount, inIterationsAreUnique, ",unique" );
10694 }
10695
10696 //===========================================================================================================================
10697 // _GAIPerfWriteLocalHostTestCaseTitle
10698 //===========================================================================================================================
10699
10700 static void
10701 _GAIPerfWriteLocalHostTestCaseTitle(
10702 char inBuffer[ kTestCaseTitleBufferSize ],
10703 GAITestAddrType inRequested,
10704 unsigned int inIterationCount )
10705 {
10706 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
10707 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
10708 }
10709
10710 //===========================================================================================================================
10711 // GAIPerfAddBasicTestCases
10712 //===========================================================================================================================
10713
10714 #define kGAIPerfBasicTestSuite_AliasCount 2
10715 #define kGAIPerfBasicTestSuite_AddrCount 4
10716
10717 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
10718 {
10719 OSStatus err;
10720 GAITestCase * testCase = NULL;
10721 char title[ kTestCaseTitleBufferSize ];
10722 unsigned int i;
10723
10724 // Test Case #1:
10725 // Resolve a domain name with
10726 //
10727 // 2 CNAME records, 4 A records, and 4 AAAA records
10728 //
10729 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10730 // queries.
10731
10732 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10733 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10734 inContext->defaultIterCount, true );
10735
10736 err = GAITestCaseCreate( title, &testCase );
10737 require_noerr( err, exit );
10738
10739 for( i = 0; i < inContext->defaultIterCount; ++i )
10740 {
10741 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10742 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10743 require_noerr( err, exit );
10744 }
10745
10746 err = GAITesterAddTestCase( inContext->tester, testCase );
10747 require_noerr( err, exit );
10748 testCase = NULL;
10749
10750 // Test Case #2:
10751 // Resolve a domain name with
10752 //
10753 // 2 CNAME records, 4 A records, and 4 AAAA records
10754 //
10755 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10756 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10757 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10758
10759 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10760 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10761 inContext->defaultIterCount, false );
10762
10763 err = GAITestCaseCreate( title, &testCase );
10764 require_noerr( err, exit );
10765
10766 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10767 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10768 inContext->defaultIterCount + 1 );
10769 require_noerr( err, exit );
10770
10771 err = GAITesterAddTestCase( inContext->tester, testCase );
10772 require_noerr( err, exit );
10773 testCase = NULL;
10774
10775 // Test Case #3:
10776 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10777
10778 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10779
10780 err = GAITestCaseCreate( title, &testCase );
10781 require_noerr( err, exit );
10782
10783 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10784 inContext->defaultIterCount );
10785 require_noerr( err, exit );
10786
10787 err = GAITesterAddTestCase( inContext->tester, testCase );
10788 require_noerr( err, exit );
10789 testCase = NULL;
10790
10791 exit:
10792 if( testCase ) GAITestCaseFree( testCase );
10793 return( err );
10794 }
10795
10796 //===========================================================================================================================
10797 // GAIPerfTesterStopHandler
10798 //===========================================================================================================================
10799
10800 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10801 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10802 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10803
10804 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10805 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10806 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10807 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10808
10809 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
10810 {
10811 OSStatus err;
10812 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10813 CFPropertyListRef plist;
10814 int exitCode;
10815
10816 err = inError;
10817 require_noerr_quiet( err, exit );
10818
10819 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
10820 "{"
10821 "%kO=" // info
10822 "{"
10823 "%kO=%lli" // callDelayMs
10824 "%kO=%lli" // serverDelayMs
10825 "%kO=%b" // skippedPathEval
10826 "%kO=%b" // usedBadUPDMode
10827 "}"
10828 "%kO=%O" // testCases
10829 "%kO=%b" // success
10830 "}",
10831 kGAIPerfResultsKey_Info,
10832 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
10833 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
10834 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
10835 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
10836 kGAIPerfResultsKey_TestCases, context->testCaseResults,
10837 kGAIPerfResultsKey_Success, !context->testFailed );
10838 require_noerr( err, exit );
10839
10840 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
10841 CFRelease( plist );
10842 require_noerr( err, exit );
10843
10844 exit:
10845 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
10846 GAIPerfContextFree( context );
10847 exit( exitCode );
10848 }
10849
10850 //===========================================================================================================================
10851 // GAIPerfResultsHandler
10852 //===========================================================================================================================
10853
10854 // Keys for test case dictionary
10855
10856 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10857 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10858 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10859 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10860 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10861 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10862 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10863
10864 // Keys for test case results array entry dictionaries
10865
10866 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10867 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10868 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10869 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10870
10871 // Keys for test case stats dictionaries
10872
10873 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10874 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10875 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10876 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10877 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10878
10879 typedef struct
10880 {
10881 double min;
10882 double max;
10883 double mean;
10884 double stdDev;
10885
10886 } GAIPerfStats;
10887
10888 #define GAIPerfStatsInit( X ) \
10889 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10890
10891 static void
10892 GAIPerfResultsHandler(
10893 const char * inCaseTitle,
10894 NanoTime64 inCaseStartTime,
10895 NanoTime64 inCaseEndTime,
10896 const GAITestItemResult * inResultArray,
10897 size_t inResultCount,
10898 void * inContext )
10899 {
10900 OSStatus err;
10901 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10902 int namesAreDynamic, namesAreUnique;
10903 const char * ptr;
10904 size_t count, startIndex;
10905 CFMutableArrayRef results = NULL;
10906 GAIPerfStats stats, firstStats, connStats;
10907 double sum, firstSum, connSum;
10908 size_t keyValueLen, i;
10909 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10910 char startTime[ 32 ];
10911 char endTime[ 32 ];
10912 const GAITestItemResult * result;
10913
10914 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10915 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10916 // the domain name's CNAME, A, and AAAA records.
10917
10918 namesAreDynamic = false;
10919 namesAreUnique = false;
10920 ptr = inCaseTitle;
10921 while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
10922 {
10923 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
10924 {
10925 namesAreDynamic = true;
10926 }
10927 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
10928 {
10929 namesAreUnique = true;
10930 }
10931 if( namesAreDynamic && namesAreUnique ) break;
10932 }
10933
10934 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
10935 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
10936 require_action( results, exit, err = kNoMemoryErr );
10937
10938 GAIPerfStatsInit( &stats );
10939 GAIPerfStatsInit( &firstStats );
10940 GAIPerfStatsInit( &connStats );
10941
10942 sum = 0.0;
10943 firstSum = 0.0;
10944 connSum = 0.0;
10945 count = 0;
10946 for( i = startIndex; i < inResultCount; ++i )
10947 {
10948 double value;
10949
10950 result = &inResultArray[ i ];
10951
10952 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
10953 "{"
10954 "%kO=%s" // name
10955 "%kO=%lli" // connectionTimeUs
10956 "%kO=%lli" // firstTimeUs
10957 "%kO=%lli" // timeUs
10958 "%kO=%lli" // error
10959 "}",
10960 kGAIPerfTestCaseResultKey_Name, result->name,
10961 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
10962 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
10963 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
10964 CFSTR( "error" ), (int64_t) result->error );
10965 require_noerr( err, exit );
10966
10967 if( !result->error )
10968 {
10969 value = (double) result->timeUs;
10970 if( value < stats.min ) stats.min = value;
10971 if( value > stats.max ) stats.max = value;
10972 sum += value;
10973
10974 value = (double) result->firstTimeUs;
10975 if( value < firstStats.min ) firstStats.min = value;
10976 if( value > firstStats.max ) firstStats.max = value;
10977 firstSum += value;
10978
10979 value = (double) result->connectionTimeUs;
10980 if( value < connStats.min ) connStats.min = value;
10981 if( value > connStats.max ) connStats.max = value;
10982 connSum += value;
10983
10984 ++count;
10985 }
10986 else
10987 {
10988 context->testFailed = true;
10989 }
10990 }
10991
10992 if( count > 0 )
10993 {
10994 stats.mean = sum / count;
10995 firstStats.mean = firstSum / count;
10996 connStats.mean = connSum / count;
10997
10998 sum = 0.0;
10999 firstSum = 0.0;
11000 connSum = 0.0;
11001 for( i = startIndex; i < inResultCount; ++i )
11002 {
11003 double diff;
11004
11005 result = &inResultArray[ i ];
11006 if( result->error ) continue;
11007
11008 diff = stats.mean - (double) result->timeUs;
11009 sum += ( diff * diff );
11010
11011 diff = firstStats.mean - (double) result->firstTimeUs;
11012 firstSum += ( diff * diff );
11013
11014 diff = connStats.mean - (double) result->connectionTimeUs;
11015 connSum += ( diff * diff );
11016 }
11017 stats.stdDev = sqrt( sum / count );
11018 firstStats.stdDev = sqrt( firstSum / count );
11019 connStats.stdDev = sqrt( connSum / count );
11020 }
11021
11022 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
11023 "{"
11024 "%kO=%s"
11025 "%kO=%s"
11026 "%kO=%s"
11027 "%kO=%O"
11028 "%kO="
11029 "{"
11030 "%kO=%lli"
11031 "%kO=%f"
11032 "%kO=%f"
11033 "%kO=%f"
11034 "%kO=%f"
11035 "}"
11036 "%kO="
11037 "{"
11038 "%kO=%lli"
11039 "%kO=%f"
11040 "%kO=%f"
11041 "%kO=%f"
11042 "%kO=%f"
11043 "}"
11044 "%kO="
11045 "{"
11046 "%kO=%lli"
11047 "%kO=%f"
11048 "%kO=%f"
11049 "%kO=%f"
11050 "%kO=%f"
11051 "}"
11052 "}",
11053 kGAIPerfTestCaseKey_Title, inCaseTitle,
11054 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
11055 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
11056 kGAIPerfTestCaseKey_Results, results,
11057 kGAIPerfTestCaseKey_Stats,
11058 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11059 kGAIPerfTestCaseStatsKey_Min, stats.min,
11060 kGAIPerfTestCaseStatsKey_Max, stats.max,
11061 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
11062 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
11063 kGAIPerfTestCaseKey_FirstStats,
11064 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11065 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
11066 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
11067 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
11068 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
11069 kGAIPerfTestCaseKey_ConnectionStats,
11070 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11071 kGAIPerfTestCaseStatsKey_Min, connStats.min,
11072 kGAIPerfTestCaseStatsKey_Max, connStats.max,
11073 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
11074 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
11075 require_noerr( err, exit );
11076
11077 exit:
11078 CFReleaseNullSafe( results );
11079 if( err ) exit( 1 );
11080 }
11081
11082 //===========================================================================================================================
11083 // GAIPerfSignalHandler
11084 //===========================================================================================================================
11085
11086 static void GAIPerfSignalHandler( void *inContext )
11087 {
11088 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11089
11090 if( !context->tester ) exit( 1 );
11091 GAITesterStop( context->tester );
11092 context->tester = NULL;
11093 }
11094
11095 //===========================================================================================================================
11096 // GAITesterCreate
11097 //===========================================================================================================================
11098
11099 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11100 // possible strings to use in the Tag label.
11101
11102 #define kGAITesterTagStringLen 6
11103
11104 typedef struct GAITestItem GAITestItem;
11105 struct GAITestItem
11106 {
11107 GAITestItem * next; // Next test item in list.
11108 char * name; // Domain name to resolve.
11109 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
11110 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
11111 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
11112 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
11113 OSStatus error; // Current status/error.
11114 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
11115 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
11116 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
11117 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11118 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11119 };
11120
11121 struct GAITestCase
11122 {
11123 GAITestCase * next; // Next test case in list.
11124 GAITestItem * itemList; // List of test items.
11125 char * title; // Title of the test case.
11126 };
11127
11128 struct GAITesterPrivate
11129 {
11130 CFRuntimeBase base; // CF object base.
11131 dispatch_queue_t queue; // Serial work queue.
11132 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
11133 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
11134 GAITestCase * caseList; // List of test cases.
11135 GAITestCase * currentCase; // Pointer to the current test case.
11136 GAITestItem * currentItem; // Pointer to the current test item.
11137 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
11138 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
11139 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11140 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11141 Boolean stopped; // True if the tester has been stopped.
11142 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
11143 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
11144 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
11145 pid_t serverPID; // PID of the test DNS server.
11146 int serverDelayMs; // Additional time to have the server delay its responses by.
11147 int serverDefaultTTL; // Default TTL for the server's records.
11148 GAITesterStopHandler_f stopHandler; // User's stop handler.
11149 void * stopContext; // User's event handler context.
11150 GAITesterResultsHandler_f resultsHandler; // User's results handler.
11151 void * resultsContext; // User's results handler context.
11152
11153 // Variables for current test item.
11154
11155 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
11156 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
11157 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
11158 uint64_t connTicks; // Ticks when the connection was created.
11159 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
11160 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
11161 Boolean gotFirstResult; // True if the first result has been received.
11162 };
11163
11164 CF_CLASS_DEFINE( GAITester );
11165
11166 static void _GAITesterStartNextTest( GAITesterRef inTester );
11167 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
11168 static void _GAITesterFirstGAITimeout( void *inContext );
11169 static void _GAITesterTimeout( void *inContext );
11170 static void DNSSD_API
11171 _GAITesterFirstGAICallback(
11172 DNSServiceRef inSDRef,
11173 DNSServiceFlags inFlags,
11174 uint32_t inInterfaceIndex,
11175 DNSServiceErrorType inError,
11176 const char * inHostname,
11177 const struct sockaddr * inSockAddr,
11178 uint32_t inTTL,
11179 void * inContext );
11180 static void DNSSD_API
11181 _GAITesterGetAddrInfoCallback(
11182 DNSServiceRef inSDRef,
11183 DNSServiceFlags inFlags,
11184 uint32_t inInterfaceIndex,
11185 DNSServiceErrorType inError,
11186 const char * inHostname,
11187 const struct sockaddr * inSockAddr,
11188 uint32_t inTTL,
11189 void * inContext );
11190 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
11191
11192 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11193
11194 static OSStatus
11195 GAITestItemCreate(
11196 const char * inName,
11197 unsigned int inAddressCount,
11198 GAITestAddrType inHasAddrs,
11199 GAITestAddrType inWantAddrs,
11200 unsigned int inTimeLimitMs,
11201 GAITestItem ** outItem );
11202 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
11203 static void GAITestItemFree( GAITestItem *inItem );
11204
11205 static OSStatus
11206 GAITesterCreate(
11207 dispatch_queue_t inQueue,
11208 unsigned int inCallDelayMs,
11209 int inServerDelayMs,
11210 int inServerDefaultTTL,
11211 Boolean inSkipPathEvaluation,
11212 Boolean inBadUDPMode,
11213 GAITesterRef * outTester )
11214 {
11215 OSStatus err;
11216 GAITesterRef obj = NULL;
11217
11218 CF_OBJECT_CREATE( GAITester, obj, err, exit );
11219
11220 ReplaceDispatchQueue( &obj->queue, inQueue );
11221 obj->callDelayMs = inCallDelayMs;
11222 obj->serverPID = -1;
11223 obj->serverDelayMs = inServerDelayMs;
11224 obj->serverDefaultTTL = inServerDefaultTTL;
11225 obj->skipPathEval = inSkipPathEvaluation;
11226 obj->badUDPMode = inBadUDPMode;
11227
11228 *outTester = obj;
11229 obj = NULL;
11230 err = kNoErr;
11231
11232 exit:
11233 CFReleaseNullSafe( obj );
11234 return( err );
11235 }
11236
11237 //===========================================================================================================================
11238 // _GAITesterFinalize
11239 //===========================================================================================================================
11240
11241 static void _GAITesterFinalize( CFTypeRef inObj )
11242 {
11243 GAITesterRef const me = (GAITesterRef) inObj;
11244 GAITestCase * testCase;
11245
11246 check( !me->getAddrInfo );
11247 check( !me->connection );
11248 check( !me->timer );
11249 dispatch_forget( &me->queue );
11250 while( ( testCase = me->caseList ) != NULL )
11251 {
11252 me->caseList = testCase->next;
11253 GAITestCaseFree( testCase );
11254 }
11255 }
11256
11257 //===========================================================================================================================
11258 // GAITesterStart
11259 //===========================================================================================================================
11260
11261 static void _GAITesterStart( void *inContext );
11262 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
11263
11264 static void GAITesterStart( GAITesterRef me )
11265 {
11266 CFRetain( me );
11267 dispatch_async_f( me->queue, me, _GAITesterStart );
11268 }
11269
11270 #define kGAITesterFirstGAITimeoutSecs 4
11271
11272 static void _GAITesterStart( void *inContext )
11273 {
11274 OSStatus err;
11275 GAITesterRef const me = (GAITesterRef) inContext;
11276 DNSServiceFlags flags;
11277 char name[ 64 ];
11278 char tag[ kGAITesterTagStringLen + 1 ];
11279
11280 err = SpawnCommand( &me->serverPID, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11281 (int64_t) getpid(),
11282 me->serverDefaultTTL >= 0, " --defaultTTL ",
11283 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
11284 me->serverDelayMs >= 0, " --responseDelay ",
11285 me->serverDelayMs >= 0, me->serverDelayMs,
11286 me->badUDPMode, " --badUDPMode" );
11287 require_noerr_quiet( err, exit );
11288
11289 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test",
11290 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
11291
11292 flags = 0;
11293 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11294
11295 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
11296 _GAITesterFirstGAICallback, me );
11297 require_noerr( err, exit );
11298
11299 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
11300 require_noerr( err, exit );
11301
11302 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
11303 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
11304 _GAITesterFirstGAITimeout, me, &me->timer );
11305 require_noerr( err, exit );
11306 dispatch_resume( me->timer );
11307
11308 exit:
11309 if( err ) _GAITesterStop( me, err );
11310 }
11311
11312 //===========================================================================================================================
11313 // GAITesterStop
11314 //===========================================================================================================================
11315
11316 static void _GAITesterUserStop( void *inContext );
11317
11318 static void GAITesterStop( GAITesterRef me )
11319 {
11320 CFRetain( me );
11321 dispatch_async_f( me->queue, me, _GAITesterUserStop );
11322 }
11323
11324 static void _GAITesterUserStop( void *inContext )
11325 {
11326 GAITesterRef const me = (GAITesterRef) inContext;
11327
11328 _GAITesterStop( me, kCanceledErr );
11329 CFRelease( me );
11330 }
11331
11332 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
11333 {
11334 OSStatus err;
11335
11336 ForgetPacketCapture( &me->pcap );
11337 dispatch_source_forget( &me->timer );
11338 DNSServiceForget( &me->getAddrInfo );
11339 DNSServiceForget( &me->connection );
11340 if( me->serverPID != -1 )
11341 {
11342 err = kill( me->serverPID, SIGTERM );
11343 err = map_global_noerr_errno( err );
11344 check_noerr( err );
11345 me->serverPID = -1;
11346 }
11347
11348 if( !me->stopped )
11349 {
11350 me->stopped = true;
11351 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
11352 CFRelease( me );
11353 }
11354 }
11355
11356 //===========================================================================================================================
11357 // GAITesterAddTestCase
11358 //===========================================================================================================================
11359
11360 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
11361 {
11362 OSStatus err;
11363 GAITestCase ** ptr;
11364
11365 require_action_quiet( inCase->itemList, exit, err = kCountErr );
11366
11367 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
11368 *ptr = inCase;
11369 err = kNoErr;
11370
11371 exit:
11372 return( err );
11373 }
11374
11375 //===========================================================================================================================
11376 // GAITesterSetStopHandler
11377 //===========================================================================================================================
11378
11379 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
11380 {
11381 me->stopHandler = inStopHandler;
11382 me->stopContext = inStopContext;
11383 }
11384
11385 //===========================================================================================================================
11386 // GAITesterSetResultsHandler
11387 //===========================================================================================================================
11388
11389 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
11390 {
11391 me->resultsHandler = inResultsHandler;
11392 me->resultsContext = inResultsContext;
11393 }
11394
11395 //===========================================================================================================================
11396 // _GAITesterStartNextTest
11397 //===========================================================================================================================
11398
11399 static void _GAITesterStartNextTest( GAITesterRef me )
11400 {
11401 OSStatus err;
11402 GAITestItem * item;
11403 DNSServiceFlags flags;
11404 DNSServiceProtocol protocols;
11405 int done = false;
11406
11407 if( me->currentItem ) me->currentItem = me->currentItem->next;
11408
11409 if( !me->currentItem )
11410 {
11411 if( me->currentCase )
11412 {
11413 // No more test items means that the current test case has completed.
11414
11415 me->caseEndTime = NanoTimeGetCurrent();
11416
11417 if( me->resultsHandler )
11418 {
11419 size_t resultCount, i;
11420 GAITestItemResult * resultArray;
11421
11422 resultCount = 0;
11423 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
11424 check( resultCount > 0 );
11425
11426 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
11427 require_action( resultArray, exit, err = kNoMemoryErr );
11428
11429 item = me->currentCase->itemList;
11430 for( i = 0; i < resultCount; ++i )
11431 {
11432 resultArray[ i ].name = item->name;
11433 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
11434 resultArray[ i ].firstTimeUs = item->firstTimeUs;
11435 resultArray[ i ].timeUs = item->timeUs;
11436 resultArray[ i ].error = item->error;
11437 item = item->next;
11438 }
11439 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
11440 me->resultsContext );
11441 ForgetMem( &resultArray );
11442 }
11443
11444 me->currentCase = me->currentCase->next;
11445 if( !me->currentCase )
11446 {
11447 done = true;
11448 err = kNoErr;
11449 goto exit;
11450 }
11451 }
11452 else
11453 {
11454 me->currentCase = me->caseList;
11455 }
11456 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
11457 me->currentItem = me->currentCase->itemList;
11458 }
11459
11460 item = me->currentItem;
11461 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
11462
11463 if( !item->wantV4 ) me->bitmapV4 = 0;
11464 else if( !item->hasV4 ) me->bitmapV4 = 1;
11465 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11466 else me->bitmapV4 = ~UINT64_C( 0 );
11467
11468 if( !item->wantV6 ) me->bitmapV6 = 0;
11469 else if( !item->hasV6 ) me->bitmapV6 = 1;
11470 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11471 else me->bitmapV6 = ~UINT64_C( 0 );
11472 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
11473 me->gotFirstResult = false;
11474
11475 // Perform preliminary tasks if this is the start of a new test case.
11476
11477 if( item == me->currentCase->itemList )
11478 {
11479 // Flush mDNSResponder's cache.
11480
11481 err = systemf( NULL, "killall -HUP mDNSResponder" );
11482 require_noerr( err, exit );
11483 sleep( 1 );
11484
11485 me->caseStartTime = NanoTimeGetCurrent();
11486 me->caseEndTime = kNanoTime_Invalid;
11487 }
11488
11489 // Start a packet capture.
11490
11491 check( !me->pcap );
11492 err = _GAITesterCreatePacketCapture( &me->pcap );
11493 require_noerr( err, exit );
11494
11495 // Start timer for test item's time limit.
11496
11497 check( !me->timer );
11498 if( item->timeLimitMs > 0 )
11499 {
11500 unsigned int timeLimitMs;
11501
11502 timeLimitMs = item->timeLimitMs;
11503 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
11504 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
11505
11506 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
11507 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
11508 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
11509 require_noerr( err, exit );
11510 dispatch_resume( me->timer );
11511 }
11512
11513 // Call DNSServiceGetAddrInfo().
11514
11515 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
11516
11517 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
11518 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11519
11520 protocols = 0;
11521 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
11522 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
11523
11524 me->startTicks = UpTicks();
11525
11526 check( !me->connection );
11527 err = DNSServiceCreateConnection( &me->connection );
11528 require_noerr( err, exit );
11529
11530 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
11531 require_noerr( err, exit );
11532
11533 me->connTicks = UpTicks();
11534
11535 check( !me->getAddrInfo );
11536 me->getAddrInfo = me->connection;
11537 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
11538 _GAITesterGetAddrInfoCallback, me );
11539 require_noerr( err, exit );
11540
11541 exit:
11542 if( err || done ) _GAITesterStop( me, err );
11543 }
11544
11545 //===========================================================================================================================
11546 // _GAITesterCreatePacketCapture
11547 //===========================================================================================================================
11548
11549 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
11550 {
11551 OSStatus err;
11552 pcap_t * pcap;
11553 struct bpf_program program;
11554 char errBuf[ PCAP_ERRBUF_SIZE ];
11555
11556 pcap = pcap_create( "lo0", errBuf );
11557 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
11558
11559 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
11560 require_noerr_action( err, exit, err = kUnknownErr );
11561
11562 err = pcap_set_snaplen( pcap, 512 );
11563 require_noerr_action( err, exit, err = kUnknownErr );
11564
11565 err = pcap_set_immediate_mode( pcap, 0 );
11566 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11567
11568 err = pcap_activate( pcap );
11569 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11570
11571 err = pcap_setdirection( pcap, PCAP_D_INOUT );
11572 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11573
11574 err = pcap_setnonblock( pcap, 1, errBuf );
11575 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
11576
11577 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
11578 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11579
11580 err = pcap_setfilter( pcap, &program );
11581 pcap_freecode( &program );
11582 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11583
11584 *outPCap = pcap;
11585 pcap = NULL;
11586
11587 exit:
11588 if( pcap ) pcap_close( pcap );
11589 return( err );
11590 }
11591
11592 //===========================================================================================================================
11593 // _GAITesterFirstGAITimeout
11594 //===========================================================================================================================
11595
11596 static void _GAITesterFirstGAITimeout( void *inContext )
11597 {
11598 GAITesterRef const me = (GAITesterRef) inContext;
11599
11600 _GAITesterStop( me, kNoResourcesErr );
11601 }
11602
11603 //===========================================================================================================================
11604 // _GAITesterTimeout
11605 //===========================================================================================================================
11606
11607 static void _GAITesterTimeout( void *inContext )
11608 {
11609 GAITesterRef const me = (GAITesterRef) inContext;
11610
11611 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
11612 }
11613
11614 //===========================================================================================================================
11615 // _GAITesterFirstGAICallback
11616 //===========================================================================================================================
11617
11618 static void DNSSD_API
11619 _GAITesterFirstGAICallback(
11620 DNSServiceRef inSDRef,
11621 DNSServiceFlags inFlags,
11622 uint32_t inInterfaceIndex,
11623 DNSServiceErrorType inError,
11624 const char * inHostname,
11625 const struct sockaddr * inSockAddr,
11626 uint32_t inTTL,
11627 void * inContext )
11628 {
11629 GAITesterRef const me = (GAITesterRef) inContext;
11630
11631 Unused( inSDRef );
11632 Unused( inInterfaceIndex );
11633 Unused( inHostname );
11634 Unused( inSockAddr );
11635 Unused( inTTL );
11636
11637 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
11638 {
11639 dispatch_source_forget( &me->timer );
11640 DNSServiceForget( &me->getAddrInfo );
11641
11642 _GAITesterStartNextTest( me );
11643 }
11644 }
11645
11646 //===========================================================================================================================
11647 // _GAITesterGetAddrInfoCallback
11648 //===========================================================================================================================
11649
11650 static void DNSSD_API
11651 _GAITesterGetAddrInfoCallback(
11652 DNSServiceRef inSDRef,
11653 DNSServiceFlags inFlags,
11654 uint32_t inInterfaceIndex,
11655 DNSServiceErrorType inError,
11656 const char * inHostname,
11657 const struct sockaddr * inSockAddr,
11658 uint32_t inTTL,
11659 void * inContext )
11660 {
11661 OSStatus err;
11662 GAITesterRef const me = (GAITesterRef) inContext;
11663 GAITestItem * const item = me->currentItem;
11664 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
11665 uint64_t nowTicks;
11666 uint64_t * bitmapPtr;
11667 uint64_t bitmask;
11668 int hasAddr;
11669
11670 Unused( inSDRef );
11671 Unused( inInterfaceIndex );
11672 Unused( inHostname );
11673 Unused( inTTL );
11674
11675 nowTicks = UpTicks();
11676
11677 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
11678
11679 // Check if we were expecting an IP address result of this type.
11680
11681 if( sip->sa.sa_family == AF_INET )
11682 {
11683 bitmapPtr = &me->bitmapV4;
11684 hasAddr = item->hasV4;
11685 }
11686 else if( sip->sa.sa_family == AF_INET6 )
11687 {
11688 bitmapPtr = &me->bitmapV6;
11689 hasAddr = item->hasV6;
11690 }
11691 else
11692 {
11693 err = kTypeErr;
11694 goto exit;
11695 }
11696
11697 bitmask = 0;
11698 if( hasAddr )
11699 {
11700 uint32_t addrOffset;
11701
11702 require_noerr_action_quiet( inError, exit, err = inError );
11703
11704 if( sip->sa.sa_family == AF_INET )
11705 {
11706 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
11707
11708 if( strcasecmp( item->name, "localhost." ) == 0 )
11709 {
11710 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
11711 }
11712 else
11713 {
11714 addrOffset = addrV4 - kDNSServerBaseAddrV4;
11715 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11716 {
11717 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11718 }
11719 }
11720 }
11721 else
11722 {
11723 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
11724
11725 if( strcasecmp( item->name, "localhost." ) == 0 )
11726 {
11727 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
11728 }
11729 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
11730 {
11731 addrOffset = addrV6[ 15 ];
11732 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11733 {
11734 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11735 }
11736 }
11737 }
11738 }
11739 else
11740 {
11741 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
11742 bitmask = 1;
11743 }
11744 require_action_quiet( bitmask != 0, exit, err = kValueErr );
11745 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
11746
11747 *bitmapPtr &= ~bitmask;
11748 if( !me->gotFirstResult )
11749 {
11750 me->firstTicks = nowTicks;
11751 me->gotFirstResult = true;
11752 }
11753 err = kNoErr;
11754
11755 exit:
11756 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
11757 {
11758 me->endTicks = nowTicks;
11759 _GAITesterCompleteCurrentTest( me, err );
11760 }
11761 }
11762
11763 //===========================================================================================================================
11764 // _GAITesterCompleteCurrentTest
11765 //===========================================================================================================================
11766
11767 static OSStatus
11768 _GAITesterGetDNSMessageFromPacket(
11769 const uint8_t * inPacketPtr,
11770 size_t inPacketLen,
11771 const uint8_t ** outMsgPtr,
11772 size_t * outMsgLen );
11773
11774 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
11775 {
11776 OSStatus err;
11777 GAITestItem * const item = me->currentItem;
11778 struct timeval timeStamps[ 4 ];
11779 struct timeval * tsPtr;
11780 struct timeval * tsQA = NULL;
11781 struct timeval * tsQAAAA = NULL;
11782 struct timeval * tsRA = NULL;
11783 struct timeval * tsRAAAA = NULL;
11784 struct timeval * t1;
11785 struct timeval * t2;
11786 int64_t idleTimeUs;
11787 uint8_t name[ kDomainNameLengthMax ];
11788
11789 dispatch_source_forget( &me->timer );
11790 DNSServiceForget( &me->getAddrInfo );
11791 DNSServiceForget( &me->connection );
11792
11793 item->error = inError;
11794 if( item->error )
11795 {
11796 err = kNoErr;
11797 goto exit;
11798 }
11799
11800 err = DomainNameFromString( name, item->name, NULL );
11801 require_noerr( err, exit );
11802
11803 tsPtr = &timeStamps[ 0 ];
11804 for( ;; )
11805 {
11806 int status;
11807 struct pcap_pkthdr * pktHdr;
11808 const uint8_t * packet;
11809 const uint8_t * msgPtr;
11810 size_t msgLen;
11811 const DNSHeader * hdr;
11812 unsigned int flags;
11813 const uint8_t * ptr;
11814 uint16_t qtype, qclass;
11815 uint8_t qname[ kDomainNameLengthMax ];
11816
11817 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
11818 if( status != 1 ) break;
11819 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
11820 if( msgLen < kDNSHeaderLength ) continue;
11821
11822 hdr = (const DNSHeader *) msgPtr;
11823 flags = DNSHeaderGetFlags( hdr );
11824 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
11825 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
11826
11827 ptr = (const uint8_t *) &hdr[ 1 ];
11828 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
11829 if( qclass != kDNSServiceClass_IN ) continue;
11830 if( !DomainNameEqual( qname, name ) ) continue;
11831
11832 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
11833 {
11834 if( flags & kDNSHeaderFlag_Response )
11835 {
11836 if( tsQA && !tsRA )
11837 {
11838 tsRA = tsPtr++;
11839 *tsRA = pktHdr->ts;
11840 }
11841 }
11842 else if( !tsQA )
11843 {
11844 tsQA = tsPtr++;
11845 *tsQA = pktHdr->ts;
11846 }
11847 }
11848 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
11849 {
11850 if( flags & kDNSHeaderFlag_Response )
11851 {
11852 if( tsQAAAA && !tsRAAAA )
11853 {
11854 tsRAAAA = tsPtr++;
11855 *tsRAAAA = pktHdr->ts;
11856 }
11857 }
11858 else if( !tsQAAAA )
11859 {
11860 tsQAAAA = tsPtr++;
11861 *tsQAAAA = pktHdr->ts;
11862 }
11863 }
11864 }
11865
11866 // t1 is the time when the last query was sent.
11867
11868 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
11869 else t1 = tsQA ? tsQA : tsQAAAA;
11870
11871 // t2 is when the first response was received.
11872
11873 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
11874 else t2 = tsRA ? tsRA : tsRAAAA;
11875
11876 if( t1 && t2 )
11877 {
11878 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
11879 if( idleTimeUs < 0 ) idleTimeUs = 0;
11880 }
11881 else
11882 {
11883 idleTimeUs = 0;
11884 }
11885
11886 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
11887 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11888 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11889
11890 exit:
11891 ForgetPacketCapture( &me->pcap );
11892 if( err ) _GAITesterStop( me, err );
11893 else _GAITesterStartNextTest( me );
11894 }
11895
11896 //===========================================================================================================================
11897 // _GAITesterGetDNSMessageFromPacket
11898 //===========================================================================================================================
11899
11900 #define kHeaderSizeNullLink 4
11901 #define kHeaderSizeIPv4Min 20
11902 #define kHeaderSizeIPv6 40
11903 #define kHeaderSizeUDP 8
11904
11905 #define kIPProtocolUDP 0x11
11906
11907 static OSStatus
11908 _GAITesterGetDNSMessageFromPacket(
11909 const uint8_t * inPacketPtr,
11910 size_t inPacketLen,
11911 const uint8_t ** outMsgPtr,
11912 size_t * outMsgLen )
11913 {
11914 OSStatus err;
11915 const uint8_t * nullLink;
11916 uint32_t addressFamily;
11917 const uint8_t * ip;
11918 int ipHeaderLen;
11919 int protocol;
11920 const uint8_t * msg;
11921 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
11922
11923 nullLink = &inPacketPtr[ 0 ];
11924 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
11925 addressFamily = ReadHost32( &nullLink[ 0 ] );
11926
11927 ip = &nullLink[ kHeaderSizeNullLink ];
11928 if( addressFamily == AF_INET )
11929 {
11930 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
11931 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
11932 protocol = ip[ 9 ];
11933 }
11934 else if( addressFamily == AF_INET6 )
11935 {
11936 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
11937 ipHeaderLen = kHeaderSizeIPv6;
11938 protocol = ip[ 6 ];
11939 }
11940 else
11941 {
11942 err = kTypeErr;
11943 goto exit;
11944 }
11945 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
11946 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
11947
11948 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
11949
11950 *outMsgPtr = msg;
11951 *outMsgLen = (size_t)( end - msg );
11952 err = kNoErr;
11953
11954 exit:
11955 return( err );
11956 }
11957
11958 //===========================================================================================================================
11959 // GAITestCaseCreate
11960 //===========================================================================================================================
11961
11962 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
11963 {
11964 OSStatus err;
11965 GAITestCase * obj;
11966
11967 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
11968 require_action( obj, exit, err = kNoMemoryErr );
11969
11970 obj->title = strdup( inTitle );
11971 require_action( obj->title, exit, err = kNoMemoryErr );
11972
11973 *outCase = obj;
11974 obj = NULL;
11975 err = kNoErr;
11976
11977 exit:
11978 if( obj ) GAITestCaseFree( obj );
11979 return( err );
11980 }
11981
11982 //===========================================================================================================================
11983 // GAITestCaseFree
11984 //===========================================================================================================================
11985
11986 static void GAITestCaseFree( GAITestCase *inCase )
11987 {
11988 GAITestItem * item;
11989
11990 while( ( item = inCase->itemList ) != NULL )
11991 {
11992 inCase->itemList = item->next;
11993 GAITestItemFree( item );
11994 }
11995 ForgetMem( &inCase->title );
11996 free( inCase );
11997 }
11998
11999 //===========================================================================================================================
12000 // GAITestCaseAddItem
12001 //===========================================================================================================================
12002
12003 static OSStatus
12004 GAITestCaseAddItem(
12005 GAITestCase * inCase,
12006 unsigned int inAliasCount,
12007 unsigned int inAddressCount,
12008 int inTTL,
12009 GAITestAddrType inHasAddrs,
12010 GAITestAddrType inWantAddrs,
12011 unsigned int inTimeLimitMs,
12012 unsigned int inItemCount )
12013 {
12014 OSStatus err;
12015 GAITestItem * item;
12016 GAITestItem * item2;
12017 GAITestItem * newItemList = NULL;
12018 GAITestItem ** itemPtr;
12019 char * ptr;
12020 char * end;
12021 unsigned int i;
12022 char name[ 64 ];
12023 char tag[ kGAITesterTagStringLen + 1 ];
12024
12025 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
12026
12027 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12028
12029 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
12030 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
12031 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12032
12033 ptr = &name[ 0 ];
12034 end = &name[ countof( name ) ];
12035
12036 // Add Alias label.
12037
12038 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
12039 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
12040
12041 // Add Count label.
12042
12043 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
12044
12045 // Add TTL label.
12046
12047 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
12048
12049 // Add Tag label.
12050
12051 SNPrintF_Add( &ptr, end, "tag-%s.",
12052 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
12053
12054 // Add IPv4 or IPv6 label if necessary.
12055
12056 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
12057 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
12058
12059 // Finally, add the d.test. labels.
12060
12061 SNPrintF_Add( &ptr, end, "d.test." );
12062
12063 // Create item.
12064
12065 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
12066 require_noerr( err, exit );
12067
12068 newItemList = item;
12069 itemPtr = &item->next;
12070
12071 // Create repeat items.
12072
12073 for( i = 1; i < inItemCount; ++i )
12074 {
12075 err = GAITestItemDup( item, &item2 );
12076 require_noerr( err, exit );
12077
12078 *itemPtr = item2;
12079 itemPtr = &item2->next;
12080 }
12081
12082 // Append to test case's item list.
12083
12084 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12085 *itemPtr = newItemList;
12086 newItemList = NULL;
12087
12088 exit:
12089 while( ( item = newItemList ) != NULL )
12090 {
12091 newItemList = item->next;
12092 GAITestItemFree( item );
12093 }
12094 return( err );
12095 }
12096
12097 //===========================================================================================================================
12098 // GAITestCaseAddLocalHostItem
12099 //===========================================================================================================================
12100
12101 static OSStatus
12102 GAITestCaseAddLocalHostItem(
12103 GAITestCase * inCase,
12104 GAITestAddrType inWantAddrs,
12105 unsigned int inTimeLimitMs,
12106 unsigned int inItemCount )
12107 {
12108 OSStatus err;
12109 GAITestItem * item;
12110 GAITestItem * item2;
12111 GAITestItem * newItemList = NULL;
12112 GAITestItem ** itemPtr;
12113 unsigned int i;
12114
12115 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
12116
12117 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
12118 require_noerr( err, exit );
12119
12120 newItemList = item;
12121 itemPtr = &item->next;
12122
12123 // Create repeat items.
12124
12125 for( i = 1; i < inItemCount; ++i )
12126 {
12127 err = GAITestItemDup( item, &item2 );
12128 require_noerr( err, exit );
12129
12130 *itemPtr = item2;
12131 itemPtr = &item2->next;
12132 }
12133
12134 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12135 *itemPtr = newItemList;
12136 newItemList = NULL;
12137
12138 exit:
12139 while( ( item = newItemList ) != NULL )
12140 {
12141 newItemList = item->next;
12142 GAITestItemFree( item );
12143 }
12144 return( err );
12145 }
12146
12147 //===========================================================================================================================
12148 // GAITestItemCreate
12149 //===========================================================================================================================
12150
12151 static OSStatus
12152 GAITestItemCreate(
12153 const char * inName,
12154 unsigned int inAddressCount,
12155 GAITestAddrType inHasAddrs,
12156 GAITestAddrType inWantAddrs,
12157 unsigned int inTimeLimitMs,
12158 GAITestItem ** outItem )
12159 {
12160 OSStatus err;
12161 GAITestItem * obj = NULL;
12162
12163 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
12164 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12165 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
12166
12167 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12168 require_action( obj, exit, err = kNoMemoryErr );
12169
12170 obj->name = strdup( inName );
12171 require_action( obj->name, exit, err = kNoMemoryErr );
12172
12173 obj->addressCount = inAddressCount;
12174 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12175 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12176 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12177 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12178 obj->error = kInProgressErr;
12179 obj->timeLimitMs = inTimeLimitMs;
12180
12181 *outItem = obj;
12182 obj = NULL;
12183 err = kNoErr;
12184
12185 exit:
12186 if( obj ) GAITestItemFree( obj );
12187 return( err );
12188 }
12189
12190 //===========================================================================================================================
12191 // GAITestItemDup
12192 //===========================================================================================================================
12193
12194 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
12195 {
12196 OSStatus err;
12197 GAITestItem * obj;
12198
12199 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12200 require_action( obj, exit, err = kNoMemoryErr );
12201
12202 *obj = *inItem;
12203 obj->next = NULL;
12204 if( inItem->name )
12205 {
12206 obj->name = strdup( inItem->name );
12207 require_action( obj->name, exit, err = kNoMemoryErr );
12208 }
12209
12210 *outItem = obj;
12211 obj = NULL;
12212 err = kNoErr;
12213
12214 exit:
12215 if( obj ) GAITestItemFree( obj );
12216 return( err );
12217 }
12218
12219 //===========================================================================================================================
12220 // GAITestItemFree
12221 //===========================================================================================================================
12222
12223 static void GAITestItemFree( GAITestItem *inItem )
12224 {
12225 ForgetMem( &inItem->name );
12226 free( inItem );
12227 }
12228
12229 //===========================================================================================================================
12230 // MDNSDiscoveryTestCmd
12231 //===========================================================================================================================
12232
12233 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12234
12235 typedef struct
12236 {
12237 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12238 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
12239 NanoTime64 startTime; // When the test started.
12240 NanoTime64 endTime; // When the test ended.
12241 pid_t replierPID; // PID of mDNS replier.
12242 uint32_t ifIndex; // Index of interface to run the replier on.
12243 unsigned int instanceCount; // Desired number of service instances.
12244 unsigned int txtSize; // Desired size of each service instance's TXT record data.
12245 unsigned int recordCountA; // Desired number of A records per replier hostname.
12246 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
12247 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
12248 double ucastDropRate; // Replier's probability of dropping a unicast response.
12249 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
12250 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
12251 Boolean useIPv4; // True if the replier is to use IPv4.
12252 Boolean useIPv6; // True if the replier is to use IPv6.
12253 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
12254 char * replierCommand; // Command used to run the replier.
12255 char * serviceType; // Type of services to browse for.
12256 ServiceBrowserRef browser; // Service browser.
12257 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
12258 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12259 OutputFormatType outputFormat; // Format of test results output.
12260 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
12261 char hostname[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12262 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12263
12264 } MDNSDiscoveryTestContext;
12265
12266 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
12267 static void DNSSD_API
12268 _MDNSDiscoveryTestAboutQueryCallback(
12269 DNSServiceRef inSDRef,
12270 DNSServiceFlags inFlags,
12271 uint32_t inInterfaceIndex,
12272 DNSServiceErrorType inError,
12273 const char * inFullName,
12274 uint16_t inType,
12275 uint16_t inClass,
12276 uint16_t inRDataLen,
12277 const void * inRDataPtr,
12278 uint32_t inTTL,
12279 void * inContext );
12280 static void
12281 _MDNSDiscoveryTestServiceBrowserCallback(
12282 ServiceBrowserResults * inResults,
12283 OSStatus inError,
12284 void * inContext );
12285 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
12286
12287 static void MDNSDiscoveryTestCmd( void )
12288 {
12289 OSStatus err;
12290 MDNSDiscoveryTestContext * context;
12291 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
12292
12293 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
12294 require_action( context, exit, err = kNoMemoryErr );
12295
12296 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
12297 require_noerr_quiet( err, exit );
12298
12299 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
12300 require_noerr_quiet( err, exit );
12301
12302 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
12303 require_noerr_quiet( err, exit );
12304
12305 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
12306 require_noerr_quiet( err, exit );
12307
12308 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
12309 require_noerr_quiet( err, exit );
12310
12311 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
12312 require_noerr_quiet( err, exit );
12313
12314 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
12315 require_noerr_quiet( err, exit );
12316
12317 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
12318 require_noerr_quiet( err, exit );
12319
12320 context->replierPID = -1;
12321 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
12322 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
12323 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
12324 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
12325 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
12326 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
12327 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
12328 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
12329 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
12330 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
12331 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
12332 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
12333 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
12334
12335 if( gMDNSDiscoveryTest_Interface )
12336 {
12337 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
12338 require_noerr_quiet( err, exit );
12339 }
12340 else
12341 {
12342 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
12343 require_noerr_quiet( err, exit );
12344 }
12345
12346 err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
12347 require_noerr_quiet( err, exit );
12348
12349 if( gMDNSDiscoveryTest_FlushCache )
12350 {
12351 err = CheckRootUser();
12352 require_noerr_quiet( err, exit );
12353
12354 err = systemf( NULL, "killall -HUP mDNSResponder" );
12355 require_noerr( err, exit );
12356 sleep( 1 );
12357 context->flushedCache = true;
12358 }
12359
12360 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
12361 context->hostname );
12362 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
12363
12364 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
12365 require_action( context->serviceType, exit, err = kUnknownErr );
12366
12367 ASPrintF( &context->replierCommand,
12368 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12369 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12370 (int64_t) getpid(),
12371 context->ifIndex,
12372 context->hostname,
12373 context->tag,
12374 context->instanceCount,
12375 context->recordCountA,
12376 context->recordCountAAAA,
12377 context->ucastDropRate,
12378 context->mcastDropRate,
12379 context->maxDropCount,
12380 context->noAdditionals, " --noAdditionals",
12381 context->useIPv4, " --ipv4",
12382 context->useIPv6, " --ipv6" );
12383 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
12384
12385 err = SpawnCommand( &context->replierPID, "%s", context->replierCommand );
12386 require_noerr_quiet( err, exit );
12387
12388 // Query for the replier's about TXT record. A response means it's fully up and running.
12389
12390 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
12391 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
12392 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
12393 require_noerr( err, exit );
12394
12395 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
12396 require_noerr( err, exit );
12397
12398 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
12399 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
12400 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
12401 require_noerr( err, exit );
12402 dispatch_resume( context->queryTimer );
12403
12404 context->startTime = NanoTimeGetCurrent();
12405 dispatch_main();
12406
12407 exit:
12408 exit( 1 );
12409 }
12410
12411 //===========================================================================================================================
12412 // _MDNSDiscoveryTestFirstQueryTimeout
12413 //===========================================================================================================================
12414
12415 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
12416 {
12417 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12418
12419 dispatch_source_forget( &context->queryTimer );
12420
12421 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12422 exit( 1 );
12423 }
12424
12425 //===========================================================================================================================
12426 // _MDNSDiscoveryTestAboutQueryCallback
12427 //===========================================================================================================================
12428
12429 static void DNSSD_API
12430 _MDNSDiscoveryTestAboutQueryCallback(
12431 DNSServiceRef inSDRef,
12432 DNSServiceFlags inFlags,
12433 uint32_t inInterfaceIndex,
12434 DNSServiceErrorType inError,
12435 const char * inFullName,
12436 uint16_t inType,
12437 uint16_t inClass,
12438 uint16_t inRDataLen,
12439 const void * inRDataPtr,
12440 uint32_t inTTL,
12441 void * inContext )
12442 {
12443 OSStatus err;
12444 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12445
12446 Unused( inSDRef );
12447 Unused( inInterfaceIndex );
12448 Unused( inFullName );
12449 Unused( inType );
12450 Unused( inClass );
12451 Unused( inRDataLen );
12452 Unused( inRDataPtr );
12453 Unused( inTTL );
12454
12455 err = inError;
12456 require_noerr( err, exit );
12457 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
12458
12459 DNSServiceForget( &context->query );
12460 dispatch_source_forget( &context->queryTimer );
12461
12462 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
12463 require_noerr( err, exit );
12464
12465 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
12466 require_noerr( err, exit );
12467
12468 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
12469 ServiceBrowserStart( context->browser );
12470
12471 exit:
12472 if( err ) exit( 1 );
12473 }
12474
12475 //===========================================================================================================================
12476 // _MDNSDiscoveryTestServiceBrowserCallback
12477 //===========================================================================================================================
12478
12479 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12480 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12481 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12482 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12483 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12484 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12485 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12486 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12487 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12488 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12489 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12490
12491 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12492 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12493 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12494 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12495 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12496 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12497 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12498 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12499 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12500 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12501
12502 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12503 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12504
12505 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12506 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12507 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12508 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12509 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12510 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12511 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12512
12513 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
12514 {
12515 OSStatus err;
12516 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12517 const SBRDomain * domain;
12518 const SBRServiceType * type;
12519 const SBRServiceInstance * instance;
12520 const SBRServiceInstance ** instanceArray = NULL;
12521 const SBRIPAddress * ipaddr;
12522 size_t hostnameLen;
12523 const char * ptr;
12524 const char * end;
12525 unsigned int i;
12526 uint32_t u32;
12527 CFMutableArrayRef unexpectedInstances;
12528 CFMutableArrayRef missingInstances;
12529 CFMutableArrayRef incorrectInstances;
12530 CFMutableDictionaryRef plist = NULL;
12531 CFMutableDictionaryRef badDict = NULL;
12532 CFMutableArrayRef unexpectedAddrs = NULL;
12533 CFMutableArrayRef missingAddrs = NULL;
12534 uint64_t maxResolveTimeUs;
12535 int success = false;
12536 char startTime[ 32 ];
12537 char endTime[ 32 ];
12538
12539 context->endTime = NanoTimeGetCurrent();
12540
12541 err = inError;
12542 require_noerr( err, exit );
12543
12544 _NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
12545 _NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
12546 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
12547 "{"
12548 "%kO="
12549 "{"
12550 "%kO=%s" // replierCommand
12551 "%kO=%lli" // txtSize
12552 "%kO=%lli" // instanceCount
12553 "%kO=%lli" // recordCountA
12554 "%kO=%lli" // recordCountAAAA
12555 "%kO=%s" // hostname
12556 "%kO=%b" // noAdditionals
12557 "%kO=%f" // ucastDropRate
12558 "%kO=%f" // mcastDropRate
12559 "%kO=%i" // maxDropCount
12560 "}"
12561 "%kO=%s" // startTime
12562 "%kO=%s" // endTime
12563 "%kO=%lli" // browseTimeSecs
12564 "%kO=%s" // serviceType
12565 "%kO=%b" // flushedCache
12566 "%kO=[%@]" // unexpectedInstances
12567 "%kO=[%@]" // missingInstances
12568 "%kO=[%@]" // incorrectInstances
12569 "}",
12570 kMDNSDiscoveryTestResultsKey_ReplierInfo,
12571 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
12572 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
12573 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
12574 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
12575 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
12576 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
12577 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
12578 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
12579 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
12580 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
12581 kMDNSDiscoveryTestResultsKey_StartTime, startTime,
12582 kMDNSDiscoveryTestResultsKey_EndTime, endTime,
12583 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
12584 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
12585 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
12586 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
12587 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
12588 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
12589 require_noerr( err, exit );
12590
12591 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
12592 require_action( domain, exit, err = kInternalErr );
12593
12594 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
12595 require_action( type, exit, err = kInternalErr );
12596
12597 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
12598 require_action( instanceArray, exit, err = kNoMemoryErr );
12599
12600 hostnameLen = strlen( context->hostname );
12601 for( instance = type->instanceList; instance; instance = instance->next )
12602 {
12603 unsigned int instanceNumber = 0;
12604
12605 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
12606 {
12607 ptr = &instance->name[ hostnameLen ];
12608 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
12609 {
12610 ptr += 2;
12611 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12612 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12613 {
12614 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
12615 {
12616 instanceNumber = u32;
12617 }
12618 }
12619 }
12620 else if( *ptr == '\0' )
12621 {
12622 instanceNumber = 1;
12623 }
12624 }
12625 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
12626 {
12627 check( !instanceArray[ instanceNumber - 1 ] );
12628 instanceArray[ instanceNumber - 1 ] = instance;
12629 }
12630 else
12631 {
12632 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
12633 "{"
12634 "%kO=%s"
12635 "%kO=%lli"
12636 "}",
12637 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
12638 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
12639 require_noerr( err, exit );
12640 }
12641 }
12642
12643 maxResolveTimeUs = 0;
12644 for( i = 1; i <= context->instanceCount; ++i )
12645 {
12646 int isHostnameValid;
12647 int isTXTValid;
12648
12649 instance = instanceArray[ i - 1 ];
12650 if( !instance )
12651 {
12652 if( i == 1 )
12653 {
12654 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
12655 require_noerr( err, exit );
12656 }
12657 else
12658 {
12659 char * instanceName = NULL;
12660
12661 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
12662 require_action( instanceName, exit, err = kUnknownErr );
12663
12664 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
12665 free( instanceName );
12666 require_noerr( err, exit );
12667 }
12668 continue;
12669 }
12670
12671 if( !instance->hostname )
12672 {
12673 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
12674 "{"
12675 "%kO=%s"
12676 "%kO=%b"
12677 "}",
12678 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12679 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
12680 require_noerr( err, exit );
12681 continue;
12682 }
12683
12684 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
12685 require_action( badDict, exit, err = kNoMemoryErr );
12686
12687 isHostnameValid = false;
12688 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
12689 {
12690 ptr = &instance->hostname[ hostnameLen ];
12691 if( i == 1 )
12692 {
12693 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
12694 }
12695 else if( *ptr == '-' )
12696 {
12697 ++ptr;
12698 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12699 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12700 {
12701 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
12702 }
12703 }
12704 }
12705 if( !isHostnameValid )
12706 {
12707 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
12708 kSizeCString );
12709 require_noerr( err, exit );
12710 }
12711
12712 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
12713 {
12714 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
12715 require_noerr( err, exit );
12716 }
12717
12718 isTXTValid = false;
12719 if( instance->txtLen == context->txtSize )
12720 {
12721 uint8_t name[ kDomainNameLengthMax ];
12722
12723 err = DomainNameFromString( name, instance->name, NULL );
12724 require_noerr( err, exit );
12725
12726 err = DomainNameAppendString( name, type->name, NULL );
12727 require_noerr( err, exit );
12728
12729 err = DomainNameAppendString( name, "local", NULL );
12730 require_noerr( err, exit );
12731
12732 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
12733 }
12734 if( !isTXTValid )
12735 {
12736 char * hexStr = NULL;
12737
12738 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
12739 require_action( hexStr, exit, err = kUnknownErr );
12740
12741 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
12742 free( hexStr );
12743 require_noerr( err, exit );
12744 }
12745
12746 if( isHostnameValid )
12747 {
12748 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
12749 unsigned int j;
12750 uint8_t addrV4[ 4 ];
12751 uint8_t addrV6[ 16 ];
12752
12753 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
12754 else addrV4Bitmap = ~UINT64_C( 0 );
12755
12756 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
12757 else addrV6Bitmap = ~UINT64_C( 0 );
12758
12759 addrV4[ 0 ] = 0;
12760 WriteBig16( &addrV4[ 1 ], i );
12761 addrV4[ 3 ] = 0;
12762
12763 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
12764 WriteBig16( &addrV6[ 12 ], i );
12765
12766 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12767 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
12768
12769 resolveTimeUs = 0;
12770 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
12771 {
12772 const uint8_t * addrPtr;
12773 unsigned int lsb;
12774 int isAddrValid = false;
12775
12776 if( ipaddr->sip.sa.sa_family == AF_INET )
12777 {
12778 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
12779 lsb = addrPtr[ 3 ];
12780 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
12781 {
12782 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12783 addrV4Bitmap &= ~bitmask;
12784 isAddrValid = true;
12785 }
12786 }
12787 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
12788 {
12789 addrPtr = ipaddr->sip.v6.sin6_addr.s6_addr;
12790 lsb = addrPtr[ 15 ];
12791 if( ( memcmp( addrPtr, addrV6, 15 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
12792 {
12793 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12794 addrV6Bitmap &= ~bitmask;
12795 isAddrValid = true;
12796 }
12797 }
12798 if( isAddrValid )
12799 {
12800 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
12801 }
12802 else
12803 {
12804 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
12805 require_noerr( err, exit );
12806 }
12807 }
12808
12809 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
12810 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
12811
12812 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
12813 {
12814 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
12815 }
12816 ForgetCF( &unexpectedAddrs );
12817
12818 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12819 require_action( missingAddrs, exit, err = kNoMemoryErr );
12820
12821 for( j = 1; addrV4Bitmap != 0; ++j )
12822 {
12823 bitmask = UINT64_C( 1 ) << ( j - 1 );
12824 if( addrV4Bitmap & bitmask )
12825 {
12826 addrV4Bitmap &= ~bitmask;
12827 addrV4[ 3 ] = (uint8_t) j;
12828 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
12829 require_noerr( err, exit );
12830 }
12831 }
12832 for( j = 1; addrV6Bitmap != 0; ++j )
12833 {
12834 bitmask = UINT64_C( 1 ) << ( j - 1 );
12835 if( addrV6Bitmap & bitmask )
12836 {
12837 addrV6Bitmap &= ~bitmask;
12838 addrV6[ 15 ] = (uint8_t) j;
12839 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.16a", addrV6 );
12840 require_noerr( err, exit );
12841 }
12842 }
12843
12844 if( CFArrayGetCount( missingAddrs ) > 0 )
12845 {
12846 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
12847 }
12848 ForgetCF( &missingAddrs );
12849 }
12850
12851 if( CFDictionaryGetCount( badDict ) > 0 )
12852 {
12853 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12854 kSizeCString );
12855 require_noerr( err, exit );
12856
12857 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
12858 CFArrayAppendValue( incorrectInstances, badDict );
12859 }
12860 ForgetCF( &badDict );
12861 }
12862
12863 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
12864 ( CFArrayGetCount( missingInstances ) == 0 ) &&
12865 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
12866 {
12867 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
12868 require_noerr( err, exit );
12869 success = true;
12870 }
12871 else
12872 {
12873 success = false;
12874 }
12875 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
12876
12877 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
12878 require_noerr_quiet( err, exit );
12879
12880 exit:
12881 ForgetCF( &context->browser );
12882 if( context->replierPID != -1 )
12883 {
12884 kill( context->replierPID, SIGTERM );
12885 context->replierPID = -1;
12886 }
12887 FreeNullSafe( instanceArray );
12888 CFReleaseNullSafe( plist );
12889 CFReleaseNullSafe( badDict );
12890 CFReleaseNullSafe( unexpectedAddrs );
12891 CFReleaseNullSafe( missingAddrs );
12892 exit( err ? 1 : ( success ? 0 : 2 ) );
12893 }
12894
12895 //===========================================================================================================================
12896 // _MDNSDiscoveryTestTXTRecordIsValid
12897 //===========================================================================================================================
12898
12899 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
12900 {
12901 uint32_t hash;
12902 int n;
12903 const uint8_t * ptr;
12904 size_t i, wholeCount, remCount;
12905 uint8_t txtStr[ 16 ];
12906
12907 if( inTXTLen == 0 ) return( false );
12908
12909 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
12910
12911 txtStr[ 0 ] = 15;
12912 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12913 check( n == 15 );
12914
12915 ptr = inTXTPtr;
12916 wholeCount = inTXTLen / 16;
12917 for( i = 0; i < wholeCount; ++i )
12918 {
12919 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
12920 ptr += 16;
12921 }
12922
12923 remCount = inTXTLen % 16;
12924 if( remCount > 0 )
12925 {
12926 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12927 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
12928 ptr += remCount;
12929 }
12930 check( ptr == &inTXTPtr[ inTXTLen ] );
12931 return( true );
12932 }
12933
12934 //===========================================================================================================================
12935 // DotLocalTestCmd
12936 //===========================================================================================================================
12937
12938 #define kDotLocalTestPreparationTimeLimitSecs 5
12939 #define kDotLocalTestSubtestDurationSecs 5
12940
12941 // Constants for SRV record query subtest.
12942
12943 #define kDotLocalTestSRV_Priority 1
12944 #define kDotLocalTestSRV_Weight 0
12945 #define kDotLocalTestSRV_Port 80
12946 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12947 #define kDotLocalTestSRV_TargetStr "www.example.com."
12948 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12949
12950 typedef enum
12951 {
12952 kDotLocalTestState_Unset = 0,
12953 kDotLocalTestState_Preparing = 1,
12954 kDotLocalTestState_GAIMDNSOnly = 2,
12955 kDotLocalTestState_GAIDNSOnly = 3,
12956 kDotLocalTestState_GAIBoth = 4,
12957 kDotLocalTestState_GAINeither = 5,
12958 kDotLocalTestState_GAINoSuchRecord = 6,
12959 kDotLocalTestState_QuerySRV = 7,
12960 kDotLocalTestState_Done = 8
12961
12962 } DotLocalTestState;
12963
12964 typedef struct
12965 {
12966 const char * testDesc; // Description of the current subtest.
12967 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
12968 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
12969 NanoTime64 startTime; // Timestamp of when the subtest started.
12970 NanoTime64 endTime; // Timestamp of when the subtest ended.
12971 CFMutableArrayRef correctResults; // Operation results that were expected.
12972 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
12973 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
12974 OSStatus error; // Subtest's error code.
12975 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12976 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12977 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12978 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12979 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
12980 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
12981 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
12982 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
12983 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
12984 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
12985 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
12986 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
12987 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
12988
12989 } DotLocalSubtest;
12990
12991 typedef struct
12992 {
12993 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
12994 DotLocalSubtest * subtest; // Current subtest's state.
12995 DNSServiceRef connection; // Shared connection for DNS-SD operations.
12996 DNSServiceRef op; // Reference for the current DNS-SD operation.
12997 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
12998 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
12999 char * replierCmd; // Command used to invoke the mdnsreplier.
13000 char * serverCmd; // Command used to invoke the test DNS server.
13001 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
13002 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13003 NanoTime64 startTime; // Timestamp for when the test started.
13004 NanoTime64 endTime; // Timestamp for when the test ended.
13005 DotLocalTestState state; // The test's current state.
13006 pid_t replierPID; // PID of spawned mdnsreplier.
13007 pid_t serverPID; // PID of spawned test DNS server.
13008 uint32_t ifIndex; // Interface index used for mdnsreplier.
13009 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
13010 OutputFormatType outputFormat; // Format of test results output.
13011 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
13012 Boolean serverIsReady; // True if response was received for test DNS server probe query.
13013 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
13014 Boolean testFailed; // True if at least one subtest failed.
13015 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13016 // The format of this string is "dotlocal-test-<six random chars>".
13017 } DotLocalTestContext;
13018
13019 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
13020 static void DNSSD_API
13021 _DotLocalTestProbeQueryRecordCallback(
13022 DNSServiceRef inSDRef,
13023 DNSServiceFlags inFlags,
13024 uint32_t inInterfaceIndex,
13025 DNSServiceErrorType inError,
13026 const char * inFullName,
13027 uint16_t inType,
13028 uint16_t inClass,
13029 uint16_t inRDataLen,
13030 const void * inRDataPtr,
13031 uint32_t inTTL,
13032 void * inContext );
13033 static void DNSSD_API
13034 _DotLocalTestRegisterRecordCallback(
13035 DNSServiceRef inSDRef,
13036 DNSRecordRef inRecordRef,
13037 DNSServiceFlags inFlags,
13038 DNSServiceErrorType inError,
13039 void * inContext );
13040 static void _DotLocalTestTimerHandler( void *inContext );
13041 static void DNSSD_API
13042 _DotLocalTestGAICallback(
13043 DNSServiceRef inSDRef,
13044 DNSServiceFlags inFlags,
13045 uint32_t inInterfaceIndex,
13046 DNSServiceErrorType inError,
13047 const char * inHostname,
13048 const struct sockaddr * inSockAddr,
13049 uint32_t inTTL,
13050 void * inContext );
13051 static void DNSSD_API
13052 _DotLocalTestQueryRecordCallback(
13053 DNSServiceRef inSDRef,
13054 DNSServiceFlags inFlags,
13055 uint32_t inInterfaceIndex,
13056 DNSServiceErrorType inError,
13057 const char * inFullName,
13058 uint16_t inType,
13059 uint16_t inClass,
13060 uint16_t inRDataLen,
13061 const void * inRDataPtr,
13062 uint32_t inTTL,
13063 void * inContext );
13064
13065 static void DotLocalTestCmd( void )
13066 {
13067 OSStatus err;
13068 DotLocalTestContext * context;
13069 uint8_t * rdataPtr;
13070 size_t rdataLen;
13071 DNSServiceFlags flags;
13072 char queryName[ 64 ];
13073 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
13074
13075 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
13076 require_action( context, exit, err = kNoMemoryErr );
13077
13078 context->startTime = NanoTimeGetCurrent();
13079 context->endTime = kNanoTime_Invalid;
13080
13081 context->state = kDotLocalTestState_Preparing;
13082
13083 if( gDotLocalTest_Interface )
13084 {
13085 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
13086 require_noerr_quiet( err, exit );
13087 }
13088 else
13089 {
13090 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
13091 require_noerr_quiet( err, exit );
13092 }
13093
13094 if( gDotLocalTest_OutputFilePath )
13095 {
13096 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
13097 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
13098 }
13099
13100 err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
13101 require_noerr_quiet( err, exit );
13102
13103 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13104 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
13105
13106 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13107 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
13108
13109 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
13110 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
13111
13112 // Spawn an mdnsreplier.
13113
13114 ASPrintF( &context->replierCmd,
13115 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13116 " --countAAAA 1",
13117 (int64_t) getpid(), context->ifIndex, context->labelStr,
13118 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
13119 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
13120
13121 err = SpawnCommand( &context->replierPID, "%s", context->replierCmd );
13122 require_noerr( err, exit );
13123
13124 // Spawn a test DNS server
13125
13126 ASPrintF( &context->serverCmd,
13127 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13128 (int64_t) getpid(), context->labelStr );
13129 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
13130
13131 err = SpawnCommand( &context->serverPID, "%s", context->serverCmd );
13132 require_noerr( err, exit );
13133
13134 // Create a shared DNS-SD connection.
13135
13136 err = DNSServiceCreateConnection( &context->connection );
13137 require_noerr( err, exit );
13138
13139 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
13140 require_noerr( err, exit );
13141
13142 // Create probe query for DNS server, i.e., query for any name that has an A record.
13143
13144 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
13145
13146 flags = kDNSServiceFlagsShareConnection;
13147 #if( TARGET_OS_WATCH )
13148 flags |= kDNSServiceFlagsPathEvaluationDone;
13149 #endif
13150
13151 context->op = context->connection;
13152 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
13153 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
13154 require_noerr( err, exit );
13155
13156 // Create probe query for mdnsreplier's "about" TXT record.
13157
13158 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
13159
13160 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
13161 #if( TARGET_OS_WATCH )
13162 flags |= kDNSServiceFlagsPathEvaluationDone;
13163 #endif
13164
13165 context->op2 = context->connection;
13166 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
13167 _DotLocalTestProbeQueryRecordCallback, context );
13168 require_noerr( err, exit );
13169
13170 // Register a dummy local. SOA record.
13171
13172 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
13173 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
13174 require_noerr( err, exit );
13175
13176 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
13177 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN, 1,
13178 rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
13179 require_noerr( err, exit );
13180
13181 // Start timer for probe responses and SOA record registration.
13182
13183 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
13184 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13185 _DotLocalTestTimerHandler, context, &context->timer );
13186 require_noerr( err, exit );
13187 dispatch_resume( context->timer );
13188
13189 dispatch_main();
13190
13191 exit:
13192 if( err ) ErrQuit( 1, "error: %#m\n", err );
13193 }
13194
13195 //===========================================================================================================================
13196 // _DotLocalTestStateMachine
13197 //===========================================================================================================================
13198
13199 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
13200 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
13201 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
13202 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
13203 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
13204
13205 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
13206 {
13207 OSStatus err;
13208 DotLocalTestState nextState;
13209
13210 DNSServiceForget( &inContext->op );
13211 DNSServiceForget( &inContext->op2 );
13212 dispatch_source_forget( &inContext->timer );
13213
13214 switch( inContext->state )
13215 {
13216 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
13217 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
13218 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
13219 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
13220 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
13221 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
13222 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
13223 default: err = kStateErr; goto exit;
13224 }
13225
13226 if( inContext->state == kDotLocalTestState_Preparing )
13227 {
13228 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
13229 {
13230 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13231 YesNoStr( inContext->registeredSOA ),
13232 YesNoStr( inContext->serverIsReady ),
13233 YesNoStr( inContext->replierIsReady ) );
13234 err = kNotPreparedErr;
13235 goto exit;
13236 }
13237 }
13238 else
13239 {
13240 err = _DotLocalTestFinalizeSubtest( inContext );
13241 require_noerr( err, exit );
13242 }
13243
13244 inContext->state = nextState;
13245 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
13246 err = _DotLocalTestStartSubtest( inContext );
13247
13248 exit:
13249 if( err ) ErrQuit( 1, "error: %#m\n", err );
13250 }
13251
13252 //===========================================================================================================================
13253 // _DotLocalSubtestCreate
13254 //===========================================================================================================================
13255
13256 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
13257 {
13258 OSStatus err;
13259 DotLocalSubtest * obj;
13260
13261 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
13262 require_action( obj, exit, err = kNoMemoryErr );
13263
13264 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13265 require_action( obj->correctResults, exit, err = kNoMemoryErr );
13266
13267 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13268 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
13269
13270 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13271 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
13272
13273 *outSubtest = obj;
13274 obj = NULL;
13275 err = kNoErr;
13276
13277 exit:
13278 if( obj ) _DotLocalSubtestFree( obj );
13279 return( err );
13280 }
13281
13282 //===========================================================================================================================
13283 // _DotLocalSubtestFree
13284 //===========================================================================================================================
13285
13286 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
13287 {
13288 ForgetMem( &inSubtest->queryName );
13289 ForgetCF( &inSubtest->correctResults );
13290 ForgetCF( &inSubtest->duplicateResults );
13291 ForgetCF( &inSubtest->unexpectedResults );
13292 free( inSubtest );
13293 }
13294
13295 //===========================================================================================================================
13296 // _DotLocalTestStartSubtest
13297 //===========================================================================================================================
13298
13299 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
13300 {
13301 OSStatus err;
13302 DotLocalSubtest * subtest = NULL;
13303 DNSServiceRef op = NULL;
13304 DNSServiceFlags flags;
13305
13306 err = _DotLocalSubtestCreate( &subtest );
13307 require_noerr( err, exit );
13308
13309 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
13310 {
13311 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
13312 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13313
13314 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13315 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13316
13317 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
13318 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::2:1
13319 subtest->addrMDNSv6[ 13 ] = 2;
13320 subtest->addrMDNSv6[ 15 ] = 1;
13321
13322 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
13323 }
13324
13325 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
13326 {
13327 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
13328 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13329
13330 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13331 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13332
13333 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13334 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13335 subtest->addrDNSv6[ 15 ] = 1;
13336
13337 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
13338 }
13339
13340 else if( inContext->state == kDotLocalTestState_GAIBoth )
13341 {
13342 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
13343 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13344
13345 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13346 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13347 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13348 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13349
13350 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13351 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13352 subtest->addrDNSv6[ 15 ] = 1;
13353
13354 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
13355 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::1:1
13356 subtest->addrMDNSv6[ 13 ] = 1;
13357 subtest->addrMDNSv6[ 15 ] = 1;
13358
13359 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
13360 }
13361
13362 else if( inContext->state == kDotLocalTestState_GAINeither )
13363 {
13364 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
13365 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13366
13367 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
13368 }
13369
13370 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
13371 {
13372 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
13373 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13374
13375 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13376 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13377 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
13378 }
13379
13380 else if( inContext->state == kDotLocalTestState_QuerySRV )
13381 {
13382 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13383 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
13384 inContext->labelStr );
13385 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13386
13387 subtest->needSRV = true;
13388 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
13389 }
13390
13391 else
13392 {
13393 err = kStateErr;
13394 goto exit;
13395 }
13396
13397 // Start new operation.
13398
13399 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
13400 #if( TARGET_OS_WATCH )
13401 flags |= kDNSServiceFlagsPathEvaluationDone;
13402 #endif
13403
13404 subtest->startTime = NanoTimeGetCurrent();
13405 subtest->endTime = kNanoTime_Invalid;
13406
13407 if( inContext->state == kDotLocalTestState_QuerySRV )
13408 {
13409 op = inContext->connection;
13410 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
13411 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
13412 require_noerr( err, exit );
13413 }
13414 else
13415 {
13416 op = inContext->connection;
13417 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
13418 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
13419 require_noerr( err, exit );
13420 }
13421
13422 // Start timer.
13423
13424 check( !inContext->timer );
13425 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
13426 INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13427 _DotLocalTestTimerHandler, inContext, &inContext->timer );
13428 require_noerr( err, exit );
13429 dispatch_resume( inContext->timer );
13430
13431 check( !inContext->op );
13432 inContext->op = op;
13433 op = NULL;
13434
13435 check( !inContext->subtest );
13436 inContext->subtest = subtest;
13437 subtest = NULL;
13438
13439 exit:
13440 if( subtest ) _DotLocalSubtestFree( subtest );
13441 if( op ) DNSServiceRefDeallocate( op );
13442 return( err );
13443 }
13444
13445 //===========================================================================================================================
13446 // _DotLocalTestFinalizeSubtest
13447 //===========================================================================================================================
13448
13449 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13450 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13451 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13452 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13453 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13454 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13455 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13456 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13457 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13458 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13459 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13460 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13461 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13462 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13463 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13464
13465 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
13466 {
13467 OSStatus err;
13468 DotLocalSubtest * subtest;
13469 CFMutableDictionaryRef reportDict;
13470 CFMutableDictionaryRef resultsDict;
13471 CFMutableArrayRef missingResults, reportArray;
13472 char startTime[ 32 ];
13473 char endTime[ 32 ];
13474
13475 subtest = inContext->subtest;
13476 inContext->subtest = NULL;
13477
13478 subtest->endTime = NanoTimeGetCurrent();
13479 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
13480 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
13481
13482 reportDict = NULL;
13483 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
13484 "{"
13485 "%kO=%s" // startTime
13486 "%kO=%s" // endTime
13487 "%kO=%s" // queryName
13488 "%kO=%s" // description
13489 "%kO={%@}" // results
13490 "}",
13491 kDotLocalTestReportKey_StartTime, startTime,
13492 kDotLocalTestReportKey_EndTime, endTime,
13493 kDotLocalTestReportKey_QueryName, subtest->queryName,
13494 kDotLocalTestReportKey_Description, subtest->testDesc,
13495 kDotLocalTestReportKey_Results, &resultsDict );
13496 require_noerr( err, exit );
13497
13498 missingResults = NULL;
13499 switch( inContext->state )
13500 {
13501 case kDotLocalTestState_GAIMDNSOnly:
13502 case kDotLocalTestState_GAIDNSOnly:
13503 case kDotLocalTestState_GAIBoth:
13504 case kDotLocalTestState_GAINeither:
13505 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
13506 {
13507 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13508 "["
13509 "%.4a" // Expected DNS IPv4 address
13510 "%.16a" // Expected DNS IPv6 address
13511 "%.4a" // Expected MDNS IPv4 address
13512 "%.16a" // Expected MDNS IPv6 address
13513 "]",
13514 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
13515 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
13516 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
13517 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
13518 require_noerr( err, exit );
13519 }
13520 break;
13521
13522 case kDotLocalTestState_QuerySRV:
13523 if( subtest->needSRV )
13524 {
13525 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13526 "["
13527 "%s" // Expected SRV record data as a string.
13528 "]",
13529 kDotLocalTestSRV_ResultStr );
13530 require_noerr( err, exit );
13531 }
13532 break;
13533
13534 case kDotLocalTestState_GAINoSuchRecord:
13535 if( subtest->needDNSv4 || subtest->needDNSv6 )
13536 {
13537 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13538 "["
13539 "%s" // No Such Record (A)
13540 "%s" // No Such Record (AAAA)
13541 "]",
13542 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
13543 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
13544 require_noerr( err, exit );
13545 }
13546 break;
13547
13548 default:
13549 err = kStateErr;
13550 goto exit;
13551 }
13552
13553 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
13554
13555 if( missingResults )
13556 {
13557 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
13558 ForgetCF( &missingResults );
13559 if( !subtest->error ) subtest->error = kNotFoundErr;
13560 }
13561
13562 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
13563 {
13564 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
13565 if( !subtest->error ) subtest->error = kUnexpectedErr;
13566 }
13567
13568 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
13569 {
13570 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
13571 if( !subtest->error ) subtest->error = kDuplicateErr;
13572 }
13573
13574 if( subtest->error ) inContext->testFailed = true;
13575 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
13576 require_noerr( err, exit );
13577
13578 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
13579 CFArrayAppendValue( reportArray, reportDict );
13580
13581 exit:
13582 _DotLocalSubtestFree( subtest );
13583 CFReleaseNullSafe( reportDict );
13584 return( err );
13585 }
13586
13587 //===========================================================================================================================
13588 // _DotLocalTestFinalizeAndExit
13589 //===========================================================================================================================
13590
13591 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
13592 {
13593 OSStatus err;
13594 CFPropertyListRef plist;
13595 char timestampStart[ 32 ];
13596 char timestampEnd[ 32 ];
13597
13598 check( !inContext->subtest );
13599 inContext->endTime = NanoTimeGetCurrent();
13600
13601 if( inContext->replierPID != -1 )
13602 {
13603 kill( inContext->replierPID, SIGTERM );
13604 inContext->replierPID = -1;
13605 }
13606 if( inContext->serverPID != -1 )
13607 {
13608 kill( inContext->serverPID, SIGTERM );
13609 inContext->serverPID = -1;
13610 }
13611 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
13612 require_noerr( err, exit );
13613
13614 _NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
13615 _NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
13616
13617 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13618 "{"
13619 "%kO=%s" // startTime
13620 "%kO=%s" // endTime
13621 "%kO=%O" // testsGAI
13622 "%kO=%O" // testsQuerySRV
13623 "%kO=%b" // success
13624 "%kO=%s" // replierCmd
13625 "%kO=%s" // serverCmd
13626 "}",
13627 kDotLocalTestReportKey_StartTime, timestampStart,
13628 kDotLocalTestReportKey_EndTime, timestampEnd,
13629 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
13630 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
13631 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
13632 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
13633 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
13634 require_noerr( err, exit );
13635
13636 ForgetCF( &inContext->reportsGAI );
13637 ForgetCF( &inContext->reportsQuerySRV );
13638
13639 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
13640 CFRelease( plist );
13641 require_noerr( err, exit );
13642
13643 exit( inContext->testFailed ? 2 : 0 );
13644
13645 exit:
13646 ErrQuit( 1, "error: %#m\n", err );
13647 }
13648
13649 //===========================================================================================================================
13650 // _DotLocalTestProbeQueryRecordCallback
13651 //===========================================================================================================================
13652
13653 static void DNSSD_API
13654 _DotLocalTestProbeQueryRecordCallback(
13655 DNSServiceRef inSDRef,
13656 DNSServiceFlags inFlags,
13657 uint32_t inInterfaceIndex,
13658 DNSServiceErrorType inError,
13659 const char * inFullName,
13660 uint16_t inType,
13661 uint16_t inClass,
13662 uint16_t inRDataLen,
13663 const void * inRDataPtr,
13664 uint32_t inTTL,
13665 void * inContext )
13666 {
13667 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13668
13669 Unused( inInterfaceIndex );
13670 Unused( inFullName );
13671 Unused( inType );
13672 Unused( inClass );
13673 Unused( inRDataLen );
13674 Unused( inRDataPtr );
13675 Unused( inTTL );
13676
13677 check( context->state == kDotLocalTestState_Preparing );
13678
13679 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
13680
13681 if( inSDRef == context->op )
13682 {
13683 DNSServiceForget( &context->op );
13684 context->serverIsReady = true;
13685 }
13686 else if( inSDRef == context->op2 )
13687 {
13688 DNSServiceForget( &context->op2 );
13689 context->replierIsReady = true;
13690 }
13691
13692 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
13693 {
13694 _DotLocalTestStateMachine( context );
13695 }
13696
13697 exit:
13698 return;
13699 }
13700
13701 //===========================================================================================================================
13702 // _DotLocalTestRegisterRecordCallback
13703 //===========================================================================================================================
13704
13705 static void DNSSD_API
13706 _DotLocalTestRegisterRecordCallback(
13707 DNSServiceRef inSDRef,
13708 DNSRecordRef inRecordRef,
13709 DNSServiceFlags inFlags,
13710 DNSServiceErrorType inError,
13711 void * inContext )
13712 {
13713 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13714
13715 Unused( inSDRef );
13716 Unused( inRecordRef );
13717 Unused( inFlags );
13718
13719 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
13720
13721 if( !context->registeredSOA )
13722 {
13723 context->registeredSOA = true;
13724 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
13725 }
13726 }
13727
13728 //===========================================================================================================================
13729 // _DotLocalTestTimerHandler
13730 //===========================================================================================================================
13731
13732 static void _DotLocalTestTimerHandler( void *inContext )
13733 {
13734 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
13735 }
13736
13737 //===========================================================================================================================
13738 // _DotLocalTestGAICallback
13739 //===========================================================================================================================
13740
13741 static void DNSSD_API
13742 _DotLocalTestGAICallback(
13743 DNSServiceRef inSDRef,
13744 DNSServiceFlags inFlags,
13745 uint32_t inInterfaceIndex,
13746 DNSServiceErrorType inError,
13747 const char * inHostname,
13748 const struct sockaddr * inSockAddr,
13749 uint32_t inTTL,
13750 void * inContext )
13751 {
13752 OSStatus err;
13753 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13754 DotLocalSubtest * const subtest = context->subtest;
13755 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
13756
13757 Unused( inSDRef );
13758 Unused( inInterfaceIndex );
13759 Unused( inHostname );
13760 Unused( inTTL );
13761
13762 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13763 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
13764
13765 if( context->state == kDotLocalTestState_GAINoSuchRecord )
13766 {
13767 if( inError == kDNSServiceErr_NoSuchRecord )
13768 {
13769 CFMutableArrayRef array = NULL;
13770 const char * noSuchRecordStr;
13771
13772 if( sip->sa.sa_family == AF_INET )
13773 {
13774 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13775 subtest->needDNSv4 = false;
13776
13777 noSuchRecordStr = kNoSuchRecordAStr;
13778 }
13779 else
13780 {
13781 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13782 subtest->needDNSv6 = false;
13783
13784 noSuchRecordStr = kNoSuchRecordAAAAStr;
13785 }
13786 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
13787 require_noerr( err, fatal );
13788 }
13789 else if( !inError )
13790 {
13791 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
13792 require_noerr( err, fatal );
13793 }
13794 else
13795 {
13796 err = inError;
13797 goto exit;
13798 }
13799 }
13800 else
13801 {
13802 if( !inError )
13803 {
13804 CFMutableArrayRef array = NULL;
13805
13806 if( sip->sa.sa_family == AF_INET )
13807 {
13808 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
13809
13810 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
13811 {
13812 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13813 subtest->needDNSv4 = false;
13814 }
13815 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
13816 {
13817 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13818 subtest->needMDNSv4 = false;
13819 }
13820 }
13821 else
13822 {
13823 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
13824
13825 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
13826 {
13827 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13828 subtest->needDNSv6 = false;
13829 }
13830 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
13831 {
13832 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13833 subtest->needMDNSv6 = false;
13834 }
13835 }
13836 if( !array ) array = subtest->unexpectedResults;
13837 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
13838 require_noerr( err, fatal );
13839 }
13840 else if( inError == kDNSServiceErr_NoSuchRecord )
13841 {
13842 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
13843 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
13844 require_noerr( err, fatal );
13845 }
13846 else
13847 {
13848 err = inError;
13849 goto exit;
13850 }
13851 }
13852
13853 exit:
13854 if( err )
13855 {
13856 subtest->error = err;
13857 _DotLocalTestStateMachine( context );
13858 }
13859 return;
13860
13861 fatal:
13862 ErrQuit( 1, "error: %#m\n", err );
13863 }
13864
13865 //===========================================================================================================================
13866 // _DotLocalTestQueryRecordCallback
13867 //===========================================================================================================================
13868
13869 static void DNSSD_API
13870 _DotLocalTestQueryRecordCallback(
13871 DNSServiceRef inSDRef,
13872 DNSServiceFlags inFlags,
13873 uint32_t inInterfaceIndex,
13874 DNSServiceErrorType inError,
13875 const char * inFullName,
13876 uint16_t inType,
13877 uint16_t inClass,
13878 uint16_t inRDataLen,
13879 const void * inRDataPtr,
13880 uint32_t inTTL,
13881 void * inContext )
13882 {
13883 OSStatus err;
13884 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13885 DotLocalSubtest * const subtest = context->subtest;
13886 const dns_fixed_fields_srv * fields;
13887 const uint8_t * target;
13888 const uint8_t * ptr;
13889 const uint8_t * end;
13890 char * rdataStr;
13891 unsigned int priority, weight, port;
13892 CFMutableArrayRef array;
13893
13894 Unused( inSDRef );
13895 Unused( inInterfaceIndex );
13896 Unused( inFullName );
13897 Unused( inTTL );
13898
13899 check( context->state == kDotLocalTestState_QuerySRV );
13900
13901 err = inError;
13902 require_noerr_quiet( err, exit );
13903 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13904 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
13905 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
13906
13907 fields = (const dns_fixed_fields_srv *) inRDataPtr;
13908 priority = dns_fixed_fields_srv_get_priority( fields );
13909 weight = dns_fixed_fields_srv_get_weight( fields );
13910 port = dns_fixed_fields_srv_get_port( fields );
13911 target = (const uint8_t *) &fields[ 1 ];
13912 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
13913 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
13914
13915 if( ( priority == kDotLocalTestSRV_Priority ) &&
13916 ( weight == kDotLocalTestSRV_Weight ) &&
13917 ( port == kDotLocalTestSRV_Port ) &&
13918 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
13919 {
13920 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
13921 subtest->needSRV = false;
13922 }
13923 else
13924 {
13925 array = subtest->unexpectedResults;
13926 }
13927
13928 rdataStr = NULL;
13929 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, NULL, 0, &rdataStr );
13930 if( !rdataStr )
13931 {
13932 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
13933 require_action( rdataStr, fatal, err = kNoMemoryErr );
13934 }
13935
13936 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
13937 free( rdataStr );
13938 require_noerr( err, fatal );
13939
13940 exit:
13941 if( err )
13942 {
13943 subtest->error = err;
13944 _DotLocalTestStateMachine( context );
13945 }
13946 return;
13947
13948 fatal:
13949 ErrQuit( 1, "error: %#m\n", err );
13950 }
13951
13952 //===========================================================================================================================
13953 // ProbeConflictTestCmd
13954 //===========================================================================================================================
13955
13956 #define kProbeConflictTestService_DefaultName "pctest-name"
13957 #define kProbeConflictTestService_Port 60000
13958
13959 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13960 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13961
13962 typedef struct
13963 {
13964 const char * description;
13965 const char * program;
13966 Boolean expectsRename;
13967
13968 } ProbeConflictTestCase;
13969
13970 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13971 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13972 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13973
13974 static const ProbeConflictTestCase kProbeConflictTestCases[] =
13975 {
13976 // No conflicts
13977
13978 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
13979
13980 // One multicast probe conflict
13981
13982 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
13983 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
13984 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
13985
13986 // One unicast probe conflict
13987
13988 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
13989 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
13990 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
13991
13992 // One multicast and one unicast probe conflict
13993
13994 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
13995 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
13996 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
13997 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
13998 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
13999 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14000 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
14001 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
14002 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14003
14004 // Two multicast probe conflicts
14005
14006 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
14007 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
14008 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
14009 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
14010 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
14011 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14012 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
14013 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
14014 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14015 };
14016
14017 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14018
14019 typedef struct
14020 {
14021 DNSServiceRef registration; // Test service registration.
14022 NanoTime64 testStartTime; // Test's start time.
14023 NanoTime64 startTime; // Current test case's start time.
14024 MDNSColliderRef collider; // mDNS collider object.
14025 CFMutableArrayRef results; // Array of test case results.
14026 char * serviceName; // Test service's instance name as a string. (malloced)
14027 char * serviceType; // Test service's service type as a string. (malloced)
14028 uint8_t * recordName; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14029 unsigned int testCaseIndex; // Index of the current test case.
14030 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
14031 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14032 OutputFormatType outputFormat; // Format of test report output.
14033 Boolean registered; // True if the test service instance is currently registered.
14034 Boolean testFailed; // True if at least one test case failed.
14035
14036 } ProbeConflictTestContext;
14037
14038 static void DNSSD_API
14039 _ProbeConflictTestRegisterCallback(
14040 DNSServiceRef inSDRef,
14041 DNSServiceFlags inFlags,
14042 DNSServiceErrorType inError,
14043 const char * inName,
14044 const char * inType,
14045 const char * inDomain,
14046 void * inContext );
14047 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
14048 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
14049 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
14050 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
14051
14052 static void ProbeConflictTestCmd( void )
14053 {
14054 OSStatus err;
14055 ProbeConflictTestContext * context;
14056 const char * serviceName;
14057 char tag[ 6 + 1 ];
14058
14059 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
14060 require_action( context, exit, err = kNoMemoryErr );
14061
14062 if( gProbeConflictTest_Interface )
14063 {
14064 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
14065 require_noerr_quiet( err, exit );
14066 }
14067 else
14068 {
14069 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14070 require_noerr_quiet( err, exit );
14071 }
14072
14073 if( gProbeConflictTest_OutputFilePath )
14074 {
14075 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
14076 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
14077 }
14078
14079 err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
14080 require_noerr_quiet( err, exit );
14081
14082 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
14083 require_action( context->results, exit, err = kNoMemoryErr );
14084
14085 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
14086
14087 ASPrintF( &context->serviceType, "_pctest-%s._udp",
14088 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14089 require_action( context->serviceType, exit, err = kNoMemoryErr );
14090
14091 context->testStartTime = NanoTimeGetCurrent();
14092 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
14093 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
14094 require_noerr( err, exit );
14095
14096 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
14097 require_noerr( err, exit );
14098
14099 dispatch_main();
14100
14101 exit:
14102 exit( 1 );
14103 }
14104
14105 //===========================================================================================================================
14106 // _ProbeConflictTestRegisterCallback
14107 //===========================================================================================================================
14108
14109 static void DNSSD_API
14110 _ProbeConflictTestRegisterCallback(
14111 DNSServiceRef inSDRef,
14112 DNSServiceFlags inFlags,
14113 DNSServiceErrorType inError,
14114 const char * inName,
14115 const char * inType,
14116 const char * inDomain,
14117 void * inContext )
14118 {
14119 OSStatus err;
14120 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14121
14122 Unused( inSDRef );
14123 Unused( inType );
14124 Unused( inDomain );
14125
14126 err = inError;
14127 require_noerr( err, exit );
14128
14129 if( !context->registered )
14130 {
14131 if( inFlags & kDNSServiceFlagsAdd )
14132 {
14133 uint8_t * ptr;
14134 size_t recordNameLen;
14135 unsigned int len;
14136 uint8_t name[ kDomainNameLengthMax ];
14137
14138 context->registered = true;
14139
14140 FreeNullSafe( context->serviceName );
14141 context->serviceName = strdup( inName );
14142 require_action( context->serviceName, exit, err = kNoMemoryErr );
14143
14144 err = DomainNameFromString( name, context->serviceName, NULL );
14145 require_noerr( err, exit );
14146
14147 err = DomainNameAppendString( name, context->serviceType, NULL );
14148 require_noerr( err, exit );
14149
14150 err = DomainNameAppendString( name, "local", NULL );
14151 require_noerr( err, exit );
14152
14153 ForgetMem( &context->recordName );
14154 err = DomainNameDup( name, &context->recordName, &recordNameLen );
14155 require_noerr( err, exit );
14156 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14157
14158 // Make the first label all caps so that it's easier to spot in system logs.
14159
14160 ptr = context->recordName;
14161 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
14162
14163 err = _ProbeConflictTestStartNextTest( context );
14164 require_noerr( err, exit );
14165 }
14166 }
14167 else
14168 {
14169 if( !( inFlags & kDNSServiceFlagsAdd ) )
14170 {
14171 context->registered = false;
14172 err = _ProbeConflictTestStopCurrentTest( context, true );
14173 require_noerr( err, exit );
14174 }
14175 }
14176 err = kNoErr;
14177
14178 exit:
14179 if( err ) exit( 1 );
14180 }
14181
14182 //===========================================================================================================================
14183 // _ProbeConflictTestColliderStopHandler
14184 //===========================================================================================================================
14185
14186 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
14187 {
14188 OSStatus err;
14189 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14190
14191 err = inError;
14192 require_noerr_quiet( err, exit );
14193
14194 ForgetCF( &context->collider );
14195
14196 err = _ProbeConflictTestStopCurrentTest( context, false );
14197 require_noerr( err, exit );
14198
14199 err = _ProbeConflictTestStartNextTest( context );
14200 require_noerr( err, exit );
14201
14202 exit:
14203 if( err ) exit( 1 );
14204 }
14205
14206 //===========================================================================================================================
14207 // _ProbeConflictTestStartNextTest
14208 //===========================================================================================================================
14209
14210 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
14211 {
14212 OSStatus err;
14213 const ProbeConflictTestCase * testCase;
14214
14215 check( !inContext->collider );
14216
14217 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
14218 {
14219 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14220 }
14221 else
14222 {
14223 _ProbeConflictTestFinalizeAndExit( inContext );
14224 }
14225
14226 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
14227 require_noerr( err, exit );
14228
14229 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
14230 require_noerr( err, exit );
14231
14232 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
14233 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
14234 require_noerr( err, exit );
14235
14236 MDNSColliderSetProtocols( inContext->collider, kMDNSColliderProtocol_IPv4 );
14237 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
14238 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
14239
14240 inContext->startTime = NanoTimeGetCurrent();
14241 err = MDNSColliderStart( inContext->collider );
14242 require_noerr( err, exit );
14243
14244 exit:
14245 return( err );
14246 }
14247
14248 //===========================================================================================================================
14249 // _ProbeConflictTestStopCurrentTest
14250 //===========================================================================================================================
14251
14252 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14253 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14254 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14255 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14256 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14257 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14258
14259 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
14260 {
14261 OSStatus err;
14262 const ProbeConflictTestCase * testCase;
14263 NanoTime64 now;
14264 Boolean passed;
14265 char startTime[ 32 ];
14266 char endTime[ 32 ];
14267
14268 now = NanoTimeGetCurrent();
14269
14270 if( inContext->collider )
14271 {
14272 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
14273 MDNSColliderStop( inContext->collider );
14274 CFRelease( inContext->collider );
14275 inContext->collider = NULL;
14276 }
14277
14278 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14279 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
14280 if( !passed ) inContext->testFailed = true;
14281
14282 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
14283 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14284
14285 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
14286 "{"
14287 "%kO=%s" // description
14288 "%kO=%b" // expectedRename
14289 "%kO=%s" // startTime
14290 "%kO=%s" // endTime
14291 "%kO=%s" // serviceName
14292 "%kO=%b" // passed
14293 "}",
14294 kProbeConflictTestCaseResultKey_Description, testCase->description,
14295 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
14296 kProbeConflictTestCaseResultKey_StartTime, startTime,
14297 kProbeConflictTestCaseResultKey_EndTime, endTime,
14298 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
14299 kProbeConflictTestCaseResultKey_Passed, passed );
14300 require_noerr( err, exit );
14301
14302 ++inContext->testCaseIndex;
14303
14304 exit:
14305 return( err );
14306 }
14307
14308 //===========================================================================================================================
14309 // _ProbeConflictTestFinalizeAndExit
14310 //===========================================================================================================================
14311
14312 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14313 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14314 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14315 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14316 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14317
14318 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
14319 {
14320 OSStatus err;
14321 CFPropertyListRef plist;
14322 NanoTime64 now;
14323 char startTime[ 32 ];
14324 char endTime[ 32 ];
14325
14326 now = NanoTimeGetCurrent();
14327
14328 check( !inContext->collider );
14329
14330 _NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
14331 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14332
14333 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
14334 "{"
14335 "%kO=%s" // startTime
14336 "%kO=%s" // endTime
14337 "%kO=%s" // serviceType
14338 "%kO=%O" // results
14339 "%kO=%b" // passed
14340 "}",
14341 kProbeConflictTestReportKey_StartTime, startTime,
14342 kProbeConflictTestReportKey_EndTime, endTime,
14343 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
14344 kProbeConflictTestReportKey_Results, inContext->results,
14345 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
14346 require_noerr( err, exit );
14347 ForgetCF( &inContext->results );
14348
14349 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
14350 CFRelease( plist );
14351 require_noerr( err, exit );
14352
14353 exit( inContext->testFailed ? 2 : 0 );
14354
14355 exit:
14356 ErrQuit( 1, "error: %#m\n", err );
14357 }
14358
14359 //===========================================================================================================================
14360 // ExpensiveConstrainedsTestCmd
14361 //===========================================================================================================================
14362
14363 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14364 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14365 #define LOOPBACK_INTERFACE_NAME "lo0"
14366 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14367 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14368 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14369 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14370 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14371
14372 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14373 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14374 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14375 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14376
14377 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14378 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14379 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14380 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14381 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14382 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14383 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14384 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14385 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14386 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14387
14388 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14389 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14390 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14391 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14392 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14393 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14396
14397 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14402
14403 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14404 enum ExpensiveConstrainedTestState
14405 {
14406 TEST_BEGIN,
14407 TEST_EXPENSIVE_PREPARE,
14408 TEST_EXPENSIVE, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14409 TEST_CONSTRAINED_PREPARE,
14410 TEST_CONSTRAINED, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14411 TEST_EXPENSIVE_CONSTRAINED_PREPARE,
14412 TEST_EXPENSIVE_CONSTRAINED, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14413 TEST_FAILED,
14414 TEST_SUCCEEDED
14415 };
14416 enum ExpensiveConstrainedTestOperation
14417 {
14418 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.
14419 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.
14420 NO_UPDATE // no status update notification
14421 };
14422
14423 typedef struct
14424 {
14425 uint32_t subtestIndex; // The index of parameter for the subtest
14426 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
14427 const char * name; // Hostname to resolve.
14428 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
14429 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
14430 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
14431 char ifName[IFNAMSIZ]; // Interface name for the given interface index.
14432 dispatch_source_t timer; // The test will check if the current behavior is valid, which is called by
14433 // the timer per 2s.
14434 pid_t serverPID;
14435 Boolean isExpensivePrev; // If the interface is expensive in the previous test step.
14436 Boolean isExpensiveNow; // If the interface is expensive now.
14437 Boolean isConstrainedPrev; // If the interface is constrained in the previous test step.
14438 Boolean isConstrainedNow; // If the interface is constrained now.
14439 Boolean startFromExpensive; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14440 uint8_t numOfRetries; // the number of retries we can have if the test fail
14441 struct timeval updateTime; // The time when interface status(expensive or constrained) is changed.
14442 struct timeval notificationTime; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14443 uint32_t counter; // To record how many times the test has repeated.
14444 enum ExpensiveConstrainedTestState state; // The current test state.
14445 enum ExpensiveConstrainedTestOperation expectedOperation; // the test expects this kind of notification
14446 enum ExpensiveConstrainedTestOperation operation; // represents what notification the callback function gets.
14447
14448 NanoTime64 testReport_startTime; // when the entire test starts
14449 CFMutableArrayRef subtestReport; // stores the log message for every subtest
14450 NanoTime64 subtestReport_startTime; // when the subtest starts
14451 CFMutableArrayRef subtestProgress; // one test iteration
14452 NanoTime64 subtestProgress_startTime; // when the test iteration starts
14453 CFMutableArrayRef subtestProgress_callBack; // array of ADD/REMOVE events
14454 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14455 OutputFormatType outputFormat; // Format of test report output.
14456 } ExpensiveConstrainedContext;
14457
14458 // structure that controls how the subtest is run
14459 typedef struct
14460 {
14461 const char *qname; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14462 Boolean deny_expensive; // if the query should avoid using expensive interface
14463 Boolean deny_constrained; // if the query should avoid using constrained interface
14464 Boolean start_from_expensive; // if the query should starts from using an expensive interface
14465 Boolean ipv4_query; // only allow IPv4 query
14466 Boolean ipv6_query; // only allow IPv6 query
14467 int8_t test_passed; // if the subtest passes
14468 } ExpensiveConstrainedTestParams;
14469
14470 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams[] =
14471 {
14472 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14473 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14474 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14475 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14476 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14477 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14478 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14479 // IPv4 Only
14480 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14481 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14482 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14483 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14484 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14485 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14486 // IPv6 Only
14487 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14488 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14489 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14493 };
14494
14495 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
14496 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
14497 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
14498 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
14499 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
14500 static void DNSSD_API
14501 ExpensiveConstrainedCallback(
14502 DNSServiceRef inSDRef,
14503 DNSServiceFlags inFlags,
14504 uint32_t inInterfaceIndex,
14505 DNSServiceErrorType inError,
14506 const char * inHostname,
14507 const struct sockaddr * inSockAddr,
14508 uint32_t inTTL,
14509 void * inContext );
14510 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
14511 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
14512 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
14513 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
14514 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
14515 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
14516 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
14517 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
14518 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
14519
14520 //===========================================================================================================================
14521 // ExpensiveConstrainedTestCmd
14522 //===========================================================================================================================
14523 static void ExpensiveConstrainedTestCmd( void )
14524 {
14525 OSStatus err;
14526 dispatch_source_t signalSource = NULL;
14527 ExpensiveConstrainedContext * context = NULL;
14528
14529 // Set up SIGINT handler.
14530 signal( SIGINT, SIG_IGN );
14531 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
14532 require_noerr( err, exit );
14533 dispatch_resume( signalSource );
14534
14535 // create the test context
14536 context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
14537 require_action( context, exit, err = kNoMemoryErr );
14538
14539 // get the command line option
14540 err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
14541 require_noerr_quiet( err, exit );
14542 if ( gExpensiveConstrainedTest_OutputFilePath )
14543 {
14544 context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
14545 require_noerr_quiet( context->outputFilePath, exit );
14546 }
14547
14548 // initialize context
14549 context->subtestIndex = 0;
14550 context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
14551
14552 // initialize the CFArray used to store the log
14553 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14554 context->testReport_startTime = NanoTimeGetCurrent();
14555
14556 // setup local DNS server
14557 ExpensiveConstrainedSetupLocalDNSServer( context );
14558
14559 ExpensiveConstrainedStartTestHandler( context );
14560
14561 dispatch_main();
14562
14563 exit:
14564 exit( 1 );
14565 }
14566
14567 //===========================================================================================================================
14568 // ExpensiveConstrainedSetupLocalDNSServer
14569 //===========================================================================================================================
14570
14571 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
14572 {
14573 pid_t current_pid = getpid();
14574 OSStatus err = SpawnCommand( &context->serverPID, "dnssdutil server -l --follow %d", current_pid );
14575 if (err != 0)
14576 {
14577 FPrintF( stdout, "dnssdutil server -l --follow <PID> failed, error: %d\n", err );
14578 exit( 1 );
14579 }
14580 sleep(2);
14581 }
14582
14583 //===========================================================================================================================
14584 // ExpensiveConstrainedStartTestHandler
14585 //===========================================================================================================================
14586
14587 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
14588 {
14589 // setup 3s timer
14590 ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
14591
14592 // set the event handler for the 3s timer
14593 dispatch_source_set_event_handler( context->timer, ^{
14594 ExpensiveConstrainedTestTimerEventHandler( context );
14595 } );
14596
14597 dispatch_resume( context->timer );
14598 }
14599
14600 //===========================================================================================================================
14601 // ExpensiveConstrainedStartTestHandler
14602 //===========================================================================================================================
14603
14604 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
14605 {
14606 dispatch_cancel( context->timer );
14607 dispatch_release( context->timer );
14608 context->timer = NULL;
14609 }
14610
14611 //===========================================================================================================================
14612 // ExpensiveConstrainedSetupTimer
14613 //===========================================================================================================================
14614
14615 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
14616 {
14617 // set the timer source, the event handler will be called for every "second" seconds
14618 context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
14619 if ( context->timer == NULL )
14620 {
14621 FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14622 exit( 1 );
14623 }
14624 // the first block will be put into the queue "second"s after calling dispatch_resume
14625 dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
14626 (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
14627 }
14628
14629 //===========================================================================================================================
14630 // ExpensiveConstrainedTestTimerEventHandler
14631 //===========================================================================================================================
14632
14633 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
14634 {
14635 OSStatus err;
14636 char buffer[ 1024 ];
14637 const char *errorDescription = NULL;
14638
14639 // do not log the state if we are in transition state
14640 if (context->state != TEST_BEGIN
14641 && context->state != TEST_SUCCEEDED
14642 && context->state != TEST_CONSTRAINED_PREPARE
14643 && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
14644 ExpensiveConstrainedSubtestProgressReport( context );
14645
14646 switch ( context->state ) {
14647 case TEST_BEGIN:
14648 {
14649 ExpensiveConstrainedStopTestHandler( context );
14650
14651 // clear mDNSResponder cache
14652 err = systemf( NULL, "killall -HUP mDNSResponder" );
14653 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14654
14655 // initialize the global parameters
14656 ExpensiveConstrainedInitializeContext( context );
14657
14658 // The local DNS server is set up on the local only interface.
14659 gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
14660 strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
14661
14662 // The local DNS server is unscoped, so we must set our question to unscoped.
14663 context->ifIndex = kDNSServiceInterfaceIndexAny;
14664
14665 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14666 // use the local dns server set up previously to run the test locally.
14667 require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
14668 SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
14669 errorDescription = buffer );
14670
14671 // get the quesion name
14672 context->name = gExpensiveConstrainedTest_Name;
14673
14674 // set the initial state for the interface
14675 context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
14676 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
14677 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14678 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14679
14680 // get question flag
14681 if ( gExpensiveConstrainedTest_DenyExpensive ) context->flags |= kDNSServiceFlagsDenyExpensive;
14682 if ( gExpensiveConstrainedTest_DenyConstrained ) context->flags |= kDNSServiceFlagsDenyConstrained;
14683 if ( gExpensiveConstrainedTest_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
14684 if ( gExpensiveConstrainedTest_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
14685
14686 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14687 #if( TARGET_OS_WATCH )
14688 context->flags |= kDNSServiceFlagsPathEvaluationDone;
14689 #endif
14690
14691 // start the query
14692 DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
14693
14694 // set the initial test status
14695 context->subtestReport_startTime = NanoTimeGetCurrent();
14696 context->subtestProgress_startTime = NanoTimeGetCurrent();
14697 context->state = TEST_EXPENSIVE_PREPARE; // start from expensive test
14698 context->isExpensiveNow = context->startFromExpensive ? true : false;
14699 context->isConstrainedNow = false;
14700 context->expectedOperation = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
14701 context->operation = NO_UPDATE;
14702 context->subtestProgress = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14703 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14704 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14705 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14706
14707 // set the queue where the callback will be called when there is an answer for the query
14708 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
14709 require_noerr( err, test_failed );
14710
14711 ExpensiveConstrainedStartTestHandler( context );
14712 }
14713 break;
14714 case TEST_EXPENSIVE_PREPARE:
14715 require_action( context->isConstrainedNow == false, test_failed,
14716 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14717 errorDescription = buffer );
14718 require_action( context->expectedOperation == context->operation, test_failed,
14719 errorDescription = "Operation is not expected" );
14720
14721 context->subtestProgress_startTime = NanoTimeGetCurrent();
14722 context->state = TEST_EXPENSIVE; // begin to test expensive flag
14723 context->counter = 0; // the number of test repetition that has passed
14724 context->isExpensivePrev = context->isExpensiveNow;
14725 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14726 context->isConstrainedPrev = false; // the interface is currently unconstrained
14727 context->isConstrainedNow = false; // the interface will be unconstrained in the current test
14728 if ( gExpensiveConstrainedTest_DenyExpensive )
14729 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14730 else
14731 context->expectedOperation = NO_UPDATE;
14732 context->operation = NO_UPDATE; // NO_UPDATE means the call back function has not been called
14733 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14734 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14735
14736 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14737 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14738
14739 // record the starting timestamp
14740 gettimeofday( &context->updateTime, NULL );
14741
14742 break;
14743 case TEST_EXPENSIVE:
14744 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14745 require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
14746 SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
14747 errorDescription = buffer );
14748 // constrained flag is always turned off when testing expensive
14749 require_action( context->isConstrainedNow == false, test_failed,
14750 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
14751 errorDescription = buffer );
14752 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14753
14754 context->counter++; // one test repetition has passed
14755 if ( context->counter == TEST_REPETITION ) // expensive test finished
14756 {
14757 // prepare to test constrained flag
14758 context->state = TEST_CONSTRAINED_PREPARE;
14759
14760 // reset the interface
14761 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14762 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14763
14764 context->isExpensiveNow = false;
14765 context->isConstrainedNow = false;
14766 gettimeofday( &context->updateTime, NULL );
14767 }
14768 else
14769 {
14770 context->subtestProgress_startTime = NanoTimeGetCurrent();
14771 context->isExpensivePrev = context->isExpensiveNow;
14772 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14773 if ( gExpensiveConstrainedTest_DenyExpensive )
14774 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14775 else
14776 context->expectedOperation = NO_UPDATE;
14777 context->operation = NO_UPDATE;
14778 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14779 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14780
14781 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14782 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14783
14784 gettimeofday( &context->updateTime, NULL );
14785 }
14786 break;
14787 case TEST_CONSTRAINED_PREPARE:
14788 // The interface should be inexpensive and unconstrained when the constrained test starts
14789 require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
14790 errorDescription = buffer );
14791 require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14792 errorDescription = buffer );
14793
14794 context->subtestProgress_startTime = NanoTimeGetCurrent();
14795 context->state = TEST_CONSTRAINED; // constrained interface is now under testing
14796 context->counter = 0;
14797 context->isExpensivePrev = false;
14798 context->isExpensiveNow = false;
14799 context->isConstrainedPrev = false;
14800 context->isConstrainedNow = true; // will set constrained flag on the interface
14801 if ( gExpensiveConstrainedTest_DenyConstrained )
14802 context->expectedOperation = RESULT_RMV;
14803 else
14804 context->expectedOperation = NO_UPDATE;
14805 context->operation = NO_UPDATE;
14806 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14807 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14808
14809 // change interface to the constrained one
14810 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
14811 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14812
14813 gettimeofday( &context->updateTime, NULL );
14814 break;
14815 case TEST_CONSTRAINED:
14816 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14817 require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
14818 SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
14819 errorDescription = buffer );
14820 require_action( context->isExpensiveNow == false, test_failed,
14821 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
14822 errorDescription = buffer );
14823 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
14824
14825 context->counter++;
14826 if (context->counter == TEST_REPETITION)
14827 {
14828 // test changing expensive and constrained flags at the same time
14829 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
14830
14831 // reset interface
14832 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14833 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14834
14835 context->isExpensiveNow = false;
14836 context->isConstrainedNow = false;
14837 gettimeofday( &context->updateTime, NULL );
14838 }
14839 else
14840 {
14841 context->subtestProgress_startTime = NanoTimeGetCurrent();
14842 context->isConstrainedPrev = context->isConstrainedNow;
14843 context->isConstrainedNow = !context->isConstrainedNow; // flip constrained flag
14844 if ( gExpensiveConstrainedTest_DenyConstrained )
14845 context->expectedOperation = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
14846 else
14847 context->expectedOperation = NO_UPDATE;
14848 context->operation = NO_UPDATE;
14849 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14850 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14851
14852 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
14853 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14854
14855 gettimeofday(&context->updateTime, NULL);
14856 }
14857 break;
14858 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
14859 // The interface should be inexpensive and unconstrained when the constrained test starts
14860 require_action( context->isExpensiveNow == false, test_failed,
14861 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
14862 errorDescription = buffer );
14863 require_action( context->isConstrainedNow == false, test_failed,
14864 SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14865 errorDescription = buffer );
14866
14867 // now flip expensive and constrained at the same time
14868 context->subtestProgress_startTime = NanoTimeGetCurrent();
14869 context->state = TEST_EXPENSIVE_CONSTRAINED;
14870 context->counter = 0;
14871 context->isExpensivePrev = false;
14872 context->isExpensiveNow = true;
14873 context->isConstrainedPrev = false;
14874 context->isConstrainedNow = true;
14875 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14876 context->expectedOperation = RESULT_RMV;
14877 else
14878 context->expectedOperation = NO_UPDATE;
14879 context->operation = NO_UPDATE;
14880 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14881 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14882
14883 err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
14884 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14885
14886 gettimeofday( &context->updateTime, NULL );
14887 break;
14888 case TEST_EXPENSIVE_CONSTRAINED:
14889 // expensive and constrained flag should always be changed
14890 require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
14891 SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
14892 errorDescription = buffer );
14893 require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
14894 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14895
14896 context->counter++;
14897 if ( context->counter == TEST_REPETITION )
14898 {
14899 context->state = TEST_SUCCEEDED;
14900 }
14901 else
14902 {
14903 context->subtestProgress_startTime = NanoTimeGetCurrent();
14904 context->isExpensivePrev = context->isExpensiveNow;
14905 context->isExpensiveNow = !context->isExpensiveNow;
14906 context->isConstrainedPrev = context->isConstrainedNow;
14907 context->isConstrainedNow = !context->isConstrainedNow;
14908 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14909 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14910 else
14911 context->expectedOperation = NO_UPDATE;
14912 context->operation = NO_UPDATE;
14913 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14914 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14915
14916 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
14917 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14918
14919 gettimeofday( &context->updateTime, NULL );
14920 }
14921 break;
14922 case TEST_FAILED:
14923 test_failed:
14924 ExpensiveConstrainedSubtestReport( context, errorDescription );
14925 ExpensiveConstrainedStopAndCleanTheTest( context );
14926 if ( context->numOfRetries > 0 )
14927 {
14928 context->state = TEST_BEGIN;
14929 context->numOfRetries--;
14930 break;
14931 }
14932 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
14933 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14934 {
14935 ExpensiveConstrainedFinalResultReport( context, false );
14936 exit( 2 );
14937 }
14938 if (context->timer == NULL)
14939 {
14940 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14941 ExpensiveConstrainedFinalResultReport( context, false );
14942 exit( 1 );
14943 }
14944 context->state = TEST_BEGIN;
14945 break;
14946 case TEST_SUCCEEDED:
14947 ExpensiveConstrainedSubtestReport( context, NULL );
14948 ExpensiveConstrainedStopAndCleanTheTest( context );
14949 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
14950 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14951 {
14952 // all the subtests have been run
14953 Boolean hasFailed = false;
14954 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
14955 hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
14956
14957 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
14958 exit( hasFailed ? 2 : 0 );
14959 }
14960 context->state = TEST_BEGIN;
14961 break;
14962 default:
14963 FPrintF( stdout, "unknown error\n" );
14964 exit( 1 );
14965 }
14966 }
14967
14968 //===========================================================================================================================
14969 // ExpensiveConstrainedCallback
14970 //===========================================================================================================================
14971
14972 static void DNSSD_API
14973 ExpensiveConstrainedCallback(
14974 __unused DNSServiceRef inSDRef,
14975 DNSServiceFlags inFlags,
14976 uint32_t inInterfaceIndex,
14977 DNSServiceErrorType inError,
14978 const char * inHostname,
14979 const struct sockaddr * inSockAddr,
14980 __unused uint32_t inTTL,
14981 void * inContext )
14982 {
14983 ExpensiveConstrainedContext * const context = (ExpensiveConstrainedContext *)inContext;
14984 OSStatus err;
14985 const char * addrStr;
14986 char addrStrBuf[ kSockAddrStringMaxSize ];
14987 char inFlagsDescription[ 128 ];
14988 NanoTime64 now;
14989 char nowTimestamp[ 32 ];
14990
14991 switch ( inError ) {
14992 case kDNSServiceErr_NoError:
14993 case kDNSServiceErr_NoSuchRecord:
14994 break;
14995
14996 case kDNSServiceErr_Timeout:
14997 Exit( kExitReason_Timeout );
14998
14999 default:
15000 err = inError;
15001 goto exit;
15002 }
15003
15004 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
15005 {
15006 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
15007 err = kTypeErr;
15008 goto exit;
15009 }
15010
15011 if( !inError )
15012 {
15013 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
15014 require_noerr( err, exit );
15015 addrStr = addrStrBuf;
15016 }
15017 else
15018 {
15019 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
15020 }
15021
15022 now = NanoTimeGetCurrent();
15023 _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
15024 SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
15025 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress_callBack,
15026 "{"
15027 "%kO=%s"
15028 "%kO=%s"
15029 "%kO=%s"
15030 "%kO=%lli"
15031 "%kO=%s"
15032 "}",
15033 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP, nowTimestamp,
15034 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME, inHostname,
15035 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS, inFlagsDescription,
15036 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE, (int64_t) inInterfaceIndex,
15037 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS, addrStr
15038 );
15039 require_noerr_quiet( err, exit );
15040
15041 if ( inFlags & kDNSServiceFlagsMoreComing )
15042 return;
15043
15044 if ( inFlags & kDNSServiceFlagsAdd )
15045 context->operation = RESULT_ADD;
15046 else
15047 context->operation = RESULT_RMV;
15048
15049 gettimeofday(&context->notificationTime, NULL);
15050 exit:
15051 if( err ) exit( 1 );
15052 }
15053
15054 //===========================================================================================================================
15055 // ExpensiveConstrainedInitializeContext
15056 //===========================================================================================================================
15057
15058 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
15059 {
15060 // clear the flags of the previous subtest
15061 context->flags = 0;
15062 context->protocols = 0;
15063
15064 // get the parameter for the current subtest
15065 const ExpensiveConstrainedTestParams *param = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
15066 gExpensiveConstrainedTest_Name = param->qname;
15067 gExpensiveConstrainedTest_DenyExpensive = param->deny_expensive;
15068 gExpensiveConstrainedTest_DenyConstrained = param->deny_constrained;
15069 gExpensiveConstrainedTest_StartFromExpensive = param->start_from_expensive;
15070 gExpensiveConstrainedTest_ProtocolIPv4 = param->ipv4_query;
15071 gExpensiveConstrainedTest_ProtocolIPv6 = param->ipv6_query;
15072 }
15073
15074 //===========================================================================================================================
15075 // ExpensiveConstrainedStopAndCleanTheTest
15076 //===========================================================================================================================
15077
15078 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
15079 {
15080 // Stop the ongoing query
15081 if ( context->opRef != NULL )
15082 DNSServiceRefDeallocate( context->opRef );
15083
15084 context->opRef = NULL;
15085 context->flags = 0;
15086 context->protocols = 0;
15087 }
15088
15089 //===========================================================================================================================
15090 // ExpensiveConstrainedSubtestProgressReport
15091 //===========================================================================================================================
15092
15093 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
15094 {
15095 OSStatus err;
15096 NanoTime64 now;
15097 char startTime[ 32 ];
15098 char endTime[ 32 ];
15099 char expensive[ 32 ];
15100 char constrained[ 32 ];
15101
15102 now = NanoTimeGetCurrent();
15103 _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
15104 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15105
15106 snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
15107 snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
15108
15109 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
15110 "{"
15111 "%kO=%s"
15112 "%kO=%s"
15113 "%kO=%s"
15114 "%kO=%s"
15115 "%kO=%s"
15116 "%kO=%s"
15117 "%kO=%s"
15118 "%kO=%O"
15119 "}",
15120 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME, startTime,
15121 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME, endTime,
15122 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE, ExpensiveConstrainedStateString(context->state),
15123 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT, ExpensiveConstrainedOperationString(context->expectedOperation),
15124 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT, ExpensiveConstrainedOperationString(context->operation),
15125 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW, expensive,
15126 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW, constrained,
15127 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK, context->subtestProgress_callBack
15128 );
15129 require_noerr( err, exit );
15130 ForgetCF( &context->subtestProgress_callBack );
15131 return;
15132
15133 exit:
15134 ErrQuit( 1, "error: %#m\n", err );
15135 }
15136
15137 //===========================================================================================================================
15138 // ExpensiveConstrainedFinalSubtestReport
15139 //===========================================================================================================================
15140
15141 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
15142 {
15143 OSStatus err;
15144 NanoTime64 now;
15145 char startTime[ 32 ];
15146 char endTime[ 32 ];
15147 char flagDescription[ 1024 ];
15148
15149 now = NanoTimeGetCurrent();
15150 _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
15151 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15152 SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
15153
15154 if (error_description != NULL)
15155 {
15156 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15157 "{"
15158 "%kO=%s"
15159 "%kO=%s"
15160 "%kO=%s"
15161 "%kO=%s"
15162 "%kO=%s"
15163 "%kO=%lli"
15164 "%kO=%s"
15165 "%kO=%O"
15166 "%kO=%s"
15167 "%kO=%O"
15168 "}",
15169 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15170 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15171 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15172 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15173 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15174 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15175 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15176 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Fail" ),
15177 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR, error_description,
15178 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15179 );
15180 }
15181 else
15182 {
15183 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15184 "{"
15185 "%kO=%s"
15186 "%kO=%s"
15187 "%kO=%s"
15188 "%kO=%s"
15189 "%kO=%s"
15190 "%kO=%lli"
15191 "%kO=%s"
15192 "%kO=%O"
15193 "%kO=%O"
15194 "}",
15195 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15196 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15197 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15198 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15199 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15200 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15201 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15202 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Pass" ),
15203 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15204 );
15205 }
15206
15207 require_noerr( err, exit );
15208 ForgetCF( &context->subtestProgress );
15209 return;
15210 exit:
15211 ErrQuit( 1, "error: %#m\n", err );
15212 }
15213
15214 //===========================================================================================================================
15215 // ExpensiveConstrainedFinalResultReport
15216 //===========================================================================================================================
15217
15218 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
15219 {
15220 OSStatus err;
15221 CFPropertyListRef plist;
15222 NanoTime64 now;
15223 char startTime[ 32 ];
15224 char endTime[ 32 ];
15225
15226 now = NanoTimeGetCurrent();
15227 _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
15228 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15229
15230 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15231 "{"
15232 "%kO=%s"
15233 "%kO=%s"
15234 "%kO=%b"
15235 "%kO=%O"
15236 "}",
15237 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME, startTime,
15238 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME, endTime,
15239 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED, allPassed,
15240 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT, context->subtestReport
15241 );
15242 require_noerr( err, exit );
15243 ForgetCF( &context->subtestReport );
15244
15245 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15246 CFRelease( plist );
15247 require_noerr( err, exit );
15248
15249 return;
15250 exit:
15251 ErrQuit( 1, "error: %#m\n", err );
15252 }
15253
15254 //===========================================================================================================================
15255 // ExpensiveConstrainedProtocolString
15256 //===========================================================================================================================
15257
15258 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
15259 {
15260 const char *str = NULL;
15261 switch ( protocol ) {
15262 case kDNSServiceProtocol_IPv4:
15263 str = "IPv4";
15264 break;
15265 case kDNSServiceProtocol_IPv6:
15266 str = "IPv6";
15267 break;
15268 case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
15269 str = "IPv4 & IPv6";
15270 break;
15271 default:
15272 break;
15273 }
15274 return str;
15275 }
15276
15277 //===========================================================================================================================
15278 // ExpensiveConstrainedStateString
15279 //===========================================================================================================================
15280
15281 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
15282 {
15283 const char *str = NULL;
15284 switch ( state ) {
15285 case TEST_BEGIN:
15286 str = "TEST_BEGIN";
15287 break;
15288 case TEST_EXPENSIVE_PREPARE:
15289 str = "TEST_EXPENSIVE_PREPARE";
15290 break;
15291 case TEST_EXPENSIVE:
15292 str = "TEST_EXPENSIVE";
15293 break;
15294 case TEST_CONSTRAINED_PREPARE:
15295 str = "TEST_CONSTRAINED_PREPARE";
15296 break;
15297 case TEST_CONSTRAINED:
15298 str = "TEST_CONSTRAINED";
15299 break;
15300 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
15301 str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15302 break;
15303 case TEST_EXPENSIVE_CONSTRAINED:
15304 str = "TEST_EXPENSIVE_CONSTRAINED";
15305 break;
15306 case TEST_FAILED:
15307 str = "TEST_FAILED";
15308 break;
15309 case TEST_SUCCEEDED:
15310 str = "TEST_SUCCEEDED";
15311 break;
15312 default:
15313 str = "UNKNOWN";
15314 break;
15315 }
15316
15317 return str;
15318 }
15319
15320 //===========================================================================================================================
15321 // ExpensiveConstrainedOperationString
15322 //===========================================================================================================================
15323
15324 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
15325 {
15326 const char *str = NULL;
15327 switch ( operation ) {
15328 case RESULT_ADD:
15329 str = "RESULT_ADD";
15330 break;
15331 case RESULT_RMV:
15332 str = "RESULT_RMV";
15333 break;
15334 case NO_UPDATE:
15335 str = "NO_UPDATE";
15336 break;
15337 default:
15338 str = "UNKNOWN";
15339 break;
15340 }
15341 return str;
15342 }
15343
15344 //===========================================================================================================================
15345 // expensiveConstrainedEndsWith
15346 //===========================================================================================================================
15347 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
15348 {
15349 if ( !str || !suffix )
15350 return false;
15351 size_t lenstr = strlen( str );
15352 size_t lensuffix = strlen( suffix );
15353 if ( lensuffix > lenstr )
15354 return false;
15355 return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
15356 }
15357
15358 //===========================================================================================================================
15359 // RegistrationTestCmd
15360 //===========================================================================================================================
15361
15362 typedef struct RegistrationSubtest RegistrationSubtest;
15363
15364 typedef struct
15365 {
15366 CFMutableArrayRef subtestReports; // Array of subtest reports.
15367 dispatch_source_t timer; // Timer to enforce subtest durations.
15368 dispatch_source_t sigSourceINT; // SIGINT signal handler for a clean test exit.
15369 dispatch_source_t sigSourceTERM; // SIGTERM signal handler for a clean test exit.
15370 RegistrationSubtest * subtest; // Current subtest.
15371 char * outputFilePath; // Path of test result output file. If NULL, stdout will be used.
15372 OutputFormatType outputFormat; // Format of test results output.
15373 CFStringRef computerNamePrev; // Previous ComputerName.
15374 CFStringRef localHostNamePrev; // Previous LocalHostName.
15375 NanoTime64 startTime; // Test's start time.
15376 char * computerName; // Temporary ComputerName to set during testing. (malloc'd)
15377 char * localHostName; // Temporary LocalHostName to set during testing. (malloc'd)
15378 CFStringEncoding computerNamePrevEncoding; // Previous ComputerName's encoding.
15379 int subtestIndex; // Index of current subtest.
15380 Boolean computerNameSet; // True if a temporary ComputerName was set.
15381 Boolean localHostNameSet; // True if a temporary LocalHostName was set.
15382 Boolean failed; // True if at least one non-skipped subtest failed.
15383 Boolean forBATS; // True if the test is running in a BATS environment.
15384
15385 } RegistrationTest;
15386
15387 typedef enum
15388 {
15389 kRegistrationInterfaceSet_Null = 0,
15390 kRegistrationInterfaceSet_All = 1,
15391 kRegistrationInterfaceSet_AllPlusAWDL = 2,
15392 kRegistrationInterfaceSet_LoopbackOnly = 3,
15393 kRegistrationInterfaceSet_AWDLOnly = 4
15394
15395 } RegistrationInterfaceSet;
15396
15397 typedef struct
15398 {
15399 RegistrationInterfaceSet interfaceSet; // Interfaces to register the service over.
15400 Boolean useDefaultName; // True if registration is to use the default service name.
15401 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15402
15403 } RegistrationSubtestParams;
15404
15405 static const RegistrationSubtestParams kRegistrationSubtestParams[] =
15406 {
15407 { kRegistrationInterfaceSet_All, true, false },
15408 { kRegistrationInterfaceSet_All, false, false },
15409 { kRegistrationInterfaceSet_AllPlusAWDL, true, false },
15410 { kRegistrationInterfaceSet_AllPlusAWDL, false, false },
15411 { kRegistrationInterfaceSet_LoopbackOnly, true, false },
15412 { kRegistrationInterfaceSet_LoopbackOnly, false, false },
15413 { kRegistrationInterfaceSet_AWDLOnly, true, false },
15414 { kRegistrationInterfaceSet_AWDLOnly, false, false },
15415 { kRegistrationInterfaceSet_All, true, true },
15416 { kRegistrationInterfaceSet_All, false, true },
15417 { kRegistrationInterfaceSet_AllPlusAWDL, true, true },
15418 { kRegistrationInterfaceSet_AllPlusAWDL, false, true },
15419 { kRegistrationInterfaceSet_LoopbackOnly, true, true },
15420 { kRegistrationInterfaceSet_LoopbackOnly, false, true },
15421 { kRegistrationInterfaceSet_AWDLOnly, true, true },
15422 { kRegistrationInterfaceSet_AWDLOnly, false, true }
15423 };
15424
15425 typedef struct
15426 {
15427 NanoTime64 browseResultTime; // Per-interface browse result time.
15428 NanoTime64 querySRVResultTime; // Per-interface SRV record query result time.
15429 NanoTime64 queryTXTResultTime; // Per-interface TXT record query result time.
15430
15431 } RegistrationResultTimes;
15432
15433 typedef struct
15434 {
15435 MDNSInterfaceItem base; // Underlying MDNSInterface linked-list item.
15436 RegistrationResultTimes times; // Per-interface result times.
15437
15438 } RegistrationInterfaceItem;
15439
15440 struct RegistrationSubtest
15441 {
15442 DNSServiceRef registration; // DNS-SD service registration.
15443 DNSServiceRef connection; // Shared DNS-SD connection.
15444 DNSServiceRef browse; // DNS-SD browse for service's type.
15445 DNSServiceRef querySRV; // DNS-SD query request for service's SRV record.
15446 DNSServiceRef queryTXT; // DNS-SD query request for service's TXT record.
15447 CFMutableArrayRef unexpected; // Array of unexpected registration, browse, and query results.
15448 #if( TARGET_OS_WATCH )
15449 CFMutableArrayRef ignored; // Array of unexpected, but ignored, browse and query results.
15450 #endif
15451 const char * serviceName; // Service's name.
15452 char * serviceNameCustom; // Service's name if using a custom name. (malloc'd)
15453 char * serviceType; // Service's service type. (malloc'd)
15454 size_t serviceTypeLen; // C string length of service's service type.
15455 char * serviceFQDN; // Service's FQDN, i.e., name of its SRV and TXT records.
15456 uint8_t * txtPtr; // Pointer to service's TXT record data. (malloc'd)
15457 size_t txtLen; // Length of service's TXT record data.
15458 RegistrationInterfaceItem * ifList; // If ifIndex == 0, interfaces that service should register over.
15459 RegistrationResultTimes ifTimes; // If ifIndex != 0, result times for interface with that index.
15460 RegistrationTest * test; // Pointer to parent test.
15461 NanoTime64 startTime; // Subtest's start time.
15462 char * description; // Subtest's description. (malloc'd)
15463 uint32_t ifIndex; // Interface index used for service registration.
15464 uint16_t port; // Service's port number.
15465 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15466 Boolean includeAWDL; // True if the IncludeAWDL flag was used during registration.
15467 Boolean ifIsAWDL; // True if ifIndex is the index of an AWDL interface.
15468 Boolean skipped; // True if this subtest is to be skipped.
15469 Boolean registered; // True if the test service was successfully registered.
15470 Boolean useDefaultName; // True if the service is to use the default service name.
15471 };
15472
15473 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest );
15474 static void _RegistrationTestFree( RegistrationTest *inTest );
15475 static void _RegistrationTestBegin( void *inContext );
15476 static void _RegistrationTestProceed( RegistrationTest *inTest );
15477 static OSStatus _RegistrationTestStart( RegistrationTest *inTest );
15478 static void _RegistrationTestStop( RegistrationTest *inTest );
15479 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15480 static OSStatus
15481 _RegistrationTestStartSubtest(
15482 RegistrationTest * inTest,
15483 const RegistrationSubtestParams * inParams,
15484 Boolean * outSkipped );
15485 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest );
15486 static void _RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
15487 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
15488 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
15489 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest );
15490 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest );
15491 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15492 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
15493 static OSStatus
15494 _RegistrationTestCreateRandomTXTRecord(
15495 size_t inMinLen,
15496 size_t inMaxLen,
15497 uint8_t ** outTXTPtr,
15498 size_t * outTXTLen );
15499 static void DNSSD_API
15500 _RegistrationSubtestRegisterCallback(
15501 DNSServiceRef inSDRef,
15502 DNSServiceFlags inFlags,
15503 DNSServiceErrorType inError,
15504 const char * inName,
15505 const char * inType,
15506 const char * inDomain,
15507 void * inContext );
15508 static void DNSSD_API
15509 _RegistrationSubtestBrowseCallback(
15510 DNSServiceRef inSDRef,
15511 DNSServiceFlags inFlags,
15512 uint32_t inIfIndex,
15513 DNSServiceErrorType inError,
15514 const char * inServiceName,
15515 const char * inServiceType,
15516 const char * inDomain,
15517 void * inContext );
15518 static void DNSSD_API
15519 _RegistrationSubtestQueryCallback(
15520 DNSServiceRef inSDRef,
15521 DNSServiceFlags inFlags,
15522 uint32_t inIfIndex,
15523 DNSServiceErrorType inError,
15524 const char * inName,
15525 uint16_t inType,
15526 uint16_t inClass,
15527 uint16_t inRDataLen,
15528 const void * inRDataPtr,
15529 uint32_t inTTL,
15530 void * inContext );
15531 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
15532 static RegistrationResultTimes *
15533 _RegistrationSubtestGetInterfaceResultTimes(
15534 RegistrationSubtest * inSubtest,
15535 uint32_t inIfIndex,
15536 Boolean * outIsAWDL );
15537 static void _RegistrationTestTimerHandler( void *inContext );
15538 #if( TARGET_OS_WATCH )
15539 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName );
15540 #endif
15541
15542 static void RegistrationTestCmd( void )
15543 {
15544 OSStatus err;
15545 RegistrationTest * test;
15546
15547 err = _RegistrationTestCreate( &test );
15548 require_noerr( err, exit );
15549
15550 if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
15551 if( gRegistrationTest_OutputFilePath )
15552 {
15553 test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
15554 require_action( test->outputFilePath, exit, err = kNoMemoryErr );
15555 }
15556
15557 err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
15558 require_noerr_quiet( err, exit );
15559
15560 dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
15561 dispatch_main();
15562
15563 exit:
15564 if( test ) _RegistrationTestFree( test );
15565 ErrQuit( 1, "error: %#m\n", err );
15566 }
15567
15568 //===========================================================================================================================
15569
15570 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest )
15571 {
15572 OSStatus err;
15573 RegistrationTest * obj;
15574
15575 obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
15576 require_action( obj, exit, err = kNoMemoryErr );
15577
15578 obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15579 require_action( obj->subtestReports, exit, err = kNoMemoryErr );
15580
15581 *outTest = obj;
15582 obj = NULL;
15583 err = kNoErr;
15584
15585 exit:
15586 if( obj ) _RegistrationTestFree( obj );
15587 return( err );
15588 }
15589
15590 //===========================================================================================================================
15591
15592 static void _RegistrationTestFree( RegistrationTest *inTest )
15593 {
15594 check( !inTest->timer );
15595 check( !inTest->sigSourceINT );
15596 check( !inTest->sigSourceTERM );
15597 check( !inTest->computerNameSet );
15598 check( !inTest->localHostNameSet );
15599 check( !inTest->subtest );
15600 ForgetCF( &inTest->subtestReports );
15601 ForgetMem( &inTest->outputFilePath );
15602 ForgetCF( &inTest->computerNamePrev );
15603 ForgetCF( &inTest->localHostNamePrev );
15604 ForgetMem( &inTest->computerName );
15605 ForgetMem( &inTest->localHostName );
15606 }
15607
15608 //===========================================================================================================================
15609
15610 static void _RegistrationTestBegin( void *inContext )
15611 {
15612 _RegistrationTestProceed( (RegistrationTest *) inContext );
15613 }
15614
15615 //===========================================================================================================================
15616
15617 static void _RegistrationTestProceed( RegistrationTest *inTest )
15618 {
15619 OSStatus err;
15620 Boolean skippedSubtest;
15621
15622 do
15623 {
15624 int subtestIndex;
15625
15626 if( !inTest->startTime )
15627 {
15628 err = _RegistrationTestStart( inTest );
15629 require_noerr_quiet( err, exit );
15630
15631 inTest->startTime = NanoTimeGetCurrent();
15632 }
15633 else
15634 {
15635 err = _RegistrationTestEndSubtest( inTest );
15636 require_noerr( err, exit );
15637
15638 ++inTest->subtestIndex;
15639 }
15640
15641 subtestIndex = inTest->subtestIndex;
15642 if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
15643 {
15644 err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
15645 require_noerr_quiet( err, exit );
15646 }
15647 else
15648 {
15649 _RegistrationTestEnd( inTest );
15650 }
15651
15652 } while( skippedSubtest );
15653
15654 exit:
15655 if( err ) _RegistrationTestExit( inTest, err );
15656 }
15657
15658 //===========================================================================================================================
15659
15660 static void _RegistrationTestSignalHandler( void *inContext );
15661
15662 static OSStatus _RegistrationTestStart( RegistrationTest *inTest )
15663 {
15664 OSStatus err;
15665 char tag[ 6 + 1 ];
15666
15667 // Save original ComputerName and LocalHostName.
15668
15669 check( !inTest->computerNamePrev );
15670 inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
15671 err = map_scerror( inTest->computerNamePrev );
15672 require_noerr( err, exit );
15673
15674 check( !inTest->localHostNamePrev );
15675 inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
15676 err = map_scerror( inTest->localHostNamePrev );
15677 require_noerr( err, exit );
15678
15679 // Generate a unique test ComputerName.
15680
15681 check( !inTest->computerName );
15682 ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
15683 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15684 require_action( inTest->computerName, exit, err = kNoMemoryErr );
15685
15686 // Generate a unique test LocalHostName.
15687
15688 check( !inTest->localHostName );
15689 ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
15690 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15691 require_action( inTest->localHostName, exit, err = kNoMemoryErr );
15692
15693 // Set up SIGINT signal handler.
15694
15695 signal( SIGINT, SIG_IGN );
15696 check( !inTest->sigSourceINT );
15697 err = DispatchSignalSourceCreate( SIGINT, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceINT );
15698 require_noerr( err, exit );
15699 dispatch_resume( inTest->sigSourceINT );
15700
15701 // Set up SIGTERM signal handler.
15702
15703 signal( SIGTERM, SIG_IGN );
15704 check( !inTest->sigSourceTERM );
15705 err = DispatchSignalSourceCreate( SIGTERM, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceTERM );
15706 require_noerr( err, exit );
15707 dispatch_resume( inTest->sigSourceTERM );
15708
15709 // Set test ComputerName.
15710
15711 check( !inTest->computerNameSet );
15712 err = _SetComputerNameWithUTF8CString( inTest->computerName );
15713 require_noerr( err, exit );
15714 inTest->computerNameSet = true;
15715
15716 // Set test LocalHostName.
15717
15718 check( !inTest->localHostNameSet );
15719 err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
15720 require_noerr( err, exit );
15721 inTest->localHostNameSet = true;
15722
15723 exit:
15724 if( err ) _RegistrationTestStop( inTest );
15725 return( err );
15726 }
15727
15728 static void _RegistrationTestSignalHandler( void *inContext )
15729 {
15730 RegistrationTest * const test = (RegistrationTest *) inContext;
15731
15732 FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15733
15734 _RegistrationTestExit( test, kCanceledErr );
15735 }
15736
15737 //===========================================================================================================================
15738
15739 static void _RegistrationTestStop( RegistrationTest *inTest )
15740 {
15741 OSStatus err;
15742
15743 dispatch_source_forget( &inTest->timer );
15744 dispatch_source_forget( &inTest->sigSourceINT );
15745 dispatch_source_forget( &inTest->sigSourceTERM );
15746 _RegistrationSubtestForget( &inTest->subtest );
15747 if( inTest->computerNameSet )
15748 {
15749 err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
15750 check_noerr( err );
15751 if( !err ) inTest->computerNameSet = false;
15752 }
15753 if( inTest->localHostNameSet )
15754 {
15755 err = _SetLocalHostName( inTest->localHostNamePrev );
15756 check_noerr( err );
15757 if( !err ) inTest->localHostNameSet = false;
15758 }
15759 }
15760
15761 //===========================================================================================================================
15762
15763 #define kRegistrationTestSubtestDurationSecs 5
15764
15765 static OSStatus
15766 _RegistrationTestStartSubtest(
15767 RegistrationTest * inTest,
15768 const RegistrationSubtestParams * inParams,
15769 Boolean * outSkipped )
15770 {
15771 OSStatus err;
15772 RegistrationSubtest * subtest;
15773 const char * interfaceDesc;
15774 DNSServiceFlags flags;
15775 char tag[ 6 + 1 ];
15776
15777 subtest = NULL;
15778 err = _RegistrationSubtestCreate( &subtest );
15779 require_noerr( err, exit );
15780
15781 subtest->test = inTest;
15782 subtest->useDefaultName = inParams->useDefaultName;
15783 subtest->useLODiscovery = inParams->useLODiscovery;
15784
15785 // Determine registration interfaces.
15786
15787 switch( inParams->interfaceSet )
15788 {
15789 case kRegistrationInterfaceSet_All:
15790 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15791
15792 if( !subtest->useLODiscovery )
15793 {
15794 err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
15795 require_noerr( err, exit );
15796 }
15797 interfaceDesc = "all interfaces (excluding AWDL)";
15798 break;
15799
15800 case kRegistrationInterfaceSet_AllPlusAWDL:
15801 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15802 subtest->includeAWDL = true;
15803
15804 if( !subtest->useLODiscovery )
15805 {
15806 err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
15807 require_noerr( err, exit );
15808 }
15809 interfaceDesc = "all interfaces (including AWDL)";
15810 break;
15811
15812 case kRegistrationInterfaceSet_LoopbackOnly:
15813 subtest->ifIndex = if_nametoindex( "lo0" );
15814 if( subtest->ifIndex == 0 )
15815 {
15816 FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
15817 err = kNoResourcesErr;
15818 goto exit;
15819 }
15820 interfaceDesc = "loopback interface";
15821 break;
15822
15823 case kRegistrationInterfaceSet_AWDLOnly:
15824 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
15825 if( err == kNotFoundErr )
15826 {
15827 FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
15828 subtest->skipped = true;
15829 err = kNoErr;
15830 }
15831 require_noerr( err, exit );
15832
15833 subtest->ifIsAWDL = true;
15834 interfaceDesc = "AWDL interface";
15835 break;
15836
15837 default:
15838 err = kParamErr;
15839 goto exit;
15840 }
15841
15842 // Create description.
15843
15844 ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
15845 interfaceDesc, subtest->useDefaultName ? "default" : "custom",
15846 subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
15847 require_action( subtest->description, exit, err = kNoMemoryErr );
15848
15849 if( subtest->skipped )
15850 {
15851 subtest->startTime = NanoTimeGetCurrent();
15852 }
15853 else
15854 {
15855 // Generate a service name.
15856
15857 if( subtest->useDefaultName )
15858 {
15859 subtest->serviceName = inTest->computerName;
15860 }
15861 else
15862 {
15863 ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
15864 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15865 require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
15866
15867 subtest->serviceName = subtest->serviceNameCustom;
15868 }
15869
15870 // Generate a service type.
15871
15872 ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
15873 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15874 require_action( subtest->serviceType, exit, err = kNoMemoryErr );
15875
15876 subtest->serviceTypeLen = strlen( subtest->serviceType );
15877
15878 // Create SRV and TXT record name FQDN.
15879
15880 ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
15881 require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
15882
15883 // Generate a port number.
15884
15885 subtest->port = (uint16_t) RandomRange( 60000, 65535 );
15886
15887 // Generate TXT record data.
15888
15889 err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
15890 require_noerr( err, exit );
15891
15892 // Register service.
15893
15894 subtest->startTime = NanoTimeGetCurrent();
15895
15896 flags = kDNSServiceFlagsNoAutoRename;
15897 if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
15898 err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
15899 subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
15900 NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
15901 _RegistrationSubtestRegisterCallback, subtest );
15902 require_noerr( err, exit );
15903
15904 err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
15905 require_noerr( err, exit );
15906
15907 // Start timer.
15908
15909 check( !inTest->timer );
15910 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
15911 INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15912 _RegistrationTestTimerHandler, inTest, &inTest->timer );
15913 require_noerr( err, exit );
15914 dispatch_resume( inTest->timer );
15915 }
15916
15917 *outSkipped = subtest->skipped;
15918
15919 check( !inTest->subtest );
15920 inTest->subtest = subtest;
15921 subtest = NULL;
15922
15923 exit:
15924 _RegistrationSubtestForget( &subtest );
15925 return( err );
15926 }
15927
15928 //===========================================================================================================================
15929
15930 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15931 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15932 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15933 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15934 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15935 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15936 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15937 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15938 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15939 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15940 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15941 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15942 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15943 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15944 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15945 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15946 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15947 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15948 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15949 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15950 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15951 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15952 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15953 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15954 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15955 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15956 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15957 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15958 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15959 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15960
15961 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15962 #define kRegistrationTestResultType_Query CFSTR( "query" )
15963 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15964 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15965 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15966
15967 static OSStatus
15968 _RegistrationTestAppendMissingResults(
15969 CFMutableArrayRef inMissingResults,
15970 const RegistrationResultTimes * inTimes,
15971 uint32_t inIfIndex,
15972 const char * inIfName );
15973
15974 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest )
15975 {
15976 OSStatus err;
15977 RegistrationSubtest * subtest;
15978 CFMutableDictionaryRef subtestReport;
15979 CFMutableArrayRef missing;
15980 char * txtStr;
15981 NanoTime64 now;
15982 Boolean subtestFailed;
15983 char startTime[ 32 ];
15984 char endTime[ 32 ];
15985 char ifNameBuf[ IF_NAMESIZE + 1 ];
15986
15987 now = NanoTimeGetCurrent();
15988
15989 subtest = inTest->subtest;
15990 inTest->subtest = NULL;
15991 _RegistrationSubtestStop( subtest );
15992
15993 missing = NULL;
15994 subtestReport = NULL;
15995 txtStr = NULL;
15996 if( subtest->txtPtr )
15997 {
15998 err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, NULL, 0, &txtStr );
15999 require_noerr( err, exit );
16000 }
16001 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16002 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16003 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
16004 "{"
16005 "%kO=%s" // description
16006 "%kO=%s" // startTime
16007 "%kO=%s" // endTime
16008 "%kO=%s" // serviceFQDN
16009 "%kO=%lli" // ifIndex
16010 "%kO=%s" // ifName
16011 "%kO=%lli" // port
16012 "%kO=%s" // txt
16013 "%kO=%b" // registered
16014 "%kO=%b" // usedDefaultName
16015 "%kO=%b" // usedLODiscovery
16016 "}",
16017 kRegistrationTestReportKey_Description, subtest->description,
16018 kRegistrationTestReportKey_StartTime, startTime,
16019 kRegistrationTestReportKey_EndTime, endTime,
16020 kRegistrationTestReportKey_ServiceFQDN, subtest->serviceFQDN,
16021 kRegistrationTestReportKey_InterfaceIndex, (int64_t) subtest->ifIndex,
16022 kRegistrationTestReportKey_InterfaceName, if_indextoname( subtest->ifIndex, ifNameBuf ),
16023 kRegistrationTestReportKey_Port, (int64_t) subtest->port,
16024 kRegistrationTestReportKey_TXT, txtStr,
16025 kRegistrationTestReportKey_Registered, (int) subtest->registered,
16026 kRegistrationTestReportKey_UsedDefaultName, (int) subtest->useDefaultName,
16027 kRegistrationTestReportKey_UsedLODiscovery, (int) subtest->useLODiscovery );
16028 ForgetMem( &txtStr );
16029 require_noerr( err, exit );
16030
16031 if( !subtest->skipped && subtest->registered )
16032 {
16033 missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16034 require_action( missing, exit, err = kNoMemoryErr );
16035
16036 if( subtest->ifList )
16037 {
16038 RegistrationInterfaceItem * item;
16039
16040 for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16041 {
16042 #if( TARGET_OS_WATCH )
16043 if( inTest->forBATS && item->base.isWiFi ) continue;
16044 #endif
16045 err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
16046 require_noerr( err, exit );
16047 }
16048 }
16049 else
16050 {
16051 err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
16052 require_noerr( err, exit );
16053 }
16054
16055 subtestFailed = false;
16056 if( CFArrayGetCount( missing ) > 0 )
16057 {
16058 subtestFailed = true;
16059 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
16060 }
16061 if( CFArrayGetCount( subtest->unexpected ) > 0 )
16062 {
16063 subtestFailed = true;
16064 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
16065 }
16066 #if( TARGET_OS_WATCH )
16067 if( CFArrayGetCount( subtest->ignored ) > 0 )
16068 {
16069 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
16070 }
16071 #endif
16072 }
16073 else
16074 {
16075 subtestFailed = true;
16076 }
16077
16078 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
16079 if( subtestFailed )
16080 {
16081 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
16082 if( !subtest->skipped ) inTest->failed = true;
16083 }
16084 CFArrayAppendValue( inTest->subtestReports, subtestReport );
16085
16086 exit:
16087 CFReleaseNullSafe( missing );
16088 CFReleaseNullSafe( subtestReport );
16089 _RegistrationSubtestFree( subtest );
16090 return( err );
16091 }
16092
16093 static OSStatus
16094 _RegistrationTestAppendMissingResult(
16095 CFMutableArrayRef inMissingResults,
16096 CFStringRef inType,
16097 uint32_t inIfIndex,
16098 const char * inIfName );
16099
16100 static OSStatus
16101 _RegistrationTestAppendMissingResults(
16102 CFMutableArrayRef inMissingResults,
16103 const RegistrationResultTimes * inTimes,
16104 uint32_t inIfIndex,
16105 const char * inIfName )
16106 {
16107 OSStatus err;
16108
16109 if( !inTimes->browseResultTime )
16110 {
16111 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
16112 inIfIndex, inIfName );
16113 require_noerr( err, exit );
16114 }
16115 if( !inTimes->querySRVResultTime )
16116 {
16117 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
16118 inIfIndex, inIfName );
16119 require_noerr( err, exit );
16120 }
16121 if( !inTimes->queryTXTResultTime )
16122 {
16123 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
16124 inIfIndex, inIfName );
16125 require_noerr( err, exit );
16126 }
16127 err = kNoErr;
16128
16129 exit:
16130 return( err );
16131 }
16132
16133 static OSStatus
16134 _RegistrationTestAppendMissingResult(
16135 CFMutableArrayRef inMissingResults,
16136 CFStringRef inType,
16137 uint32_t inIfIndex,
16138 const char * inIfName )
16139 {
16140 OSStatus err;
16141 char ifName[ IF_NAMESIZE + 1 ];
16142
16143 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
16144 "{"
16145 "%kO=%O" // resultType
16146 "%kO=%lli" // ifIndex
16147 "%kO=%s" // ifName
16148 "}",
16149 kRegistrationTestReportKey_ResultType, inType,
16150 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16151 kRegistrationTestReportKey_InterfaceName, inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
16152 return( err );
16153 }
16154
16155 //===========================================================================================================================
16156
16157 static void _RegistrationTestEnd( RegistrationTest *inTest )
16158 {
16159 OSStatus err;
16160 NanoTime64 now;
16161 CFPropertyListRef plist;
16162 char startTime[ 32 ];
16163 char endTime[ 32 ];
16164
16165 now = NanoTimeGetCurrent();
16166 _NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
16167 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16168
16169 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16170 "{"
16171 "%kO=%s" // startTime
16172 "%kO=%s" // endTime
16173 "%kO=%s" // computerName
16174 "%kO=%s" // localHostName
16175 "%kO=%O" // subtests
16176 "%kO=%b" // pass
16177 "}",
16178 kRegistrationTestReportKey_StartTime, startTime,
16179 kRegistrationTestReportKey_EndTime, endTime,
16180 kRegistrationTestReportKey_ComputerName, inTest->computerName,
16181 kRegistrationTestReportKey_LocalHostName, inTest->localHostName,
16182 kRegistrationTestReportKey_Subtests, inTest->subtestReports,
16183 kRegistrationTestReportKey_Pass, inTest->failed ? false : true );
16184 require_noerr( err, exit );
16185
16186 err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
16187 CFRelease( plist );
16188 require_noerr( err, exit );
16189
16190 exit:
16191 _RegistrationTestExit( inTest, err );
16192 }
16193
16194 //===========================================================================================================================
16195
16196 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
16197 {
16198 int exitCode;
16199
16200 if( inError )
16201 {
16202 FPrintF( stderr, "error: %#m\n", inError );
16203 exitCode = 1;
16204 }
16205 else
16206 {
16207 exitCode = inTest->failed ? 2 : 0;
16208 }
16209 _RegistrationTestForget( &inTest );
16210 exit( exitCode );
16211 }
16212
16213 //===========================================================================================================================
16214
16215 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
16216 {
16217 OSStatus err;
16218 RegistrationSubtest * obj;
16219
16220 obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
16221 require_action( obj, exit, err = kNoMemoryErr );
16222
16223 obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16224 require_action( obj->unexpected, exit, err = kNoMemoryErr );
16225
16226 #if( TARGET_OS_WATCH )
16227 obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16228 require_action( obj->ignored, exit, err = kNoMemoryErr );
16229 #endif
16230
16231 *outSubtest = obj;
16232 obj = NULL;
16233 err = kNoErr;
16234
16235 exit:
16236 if( obj ) _RegistrationSubtestFree( obj );
16237 return( err );
16238 }
16239
16240 //===========================================================================================================================
16241
16242 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest )
16243 {
16244 check( !inSubtest->registration );
16245 check( !inSubtest->browse );
16246 check( !inSubtest->querySRV );
16247 check( !inSubtest->queryTXT );
16248 check( !inSubtest->connection );
16249 ForgetMem( &inSubtest->serviceNameCustom );
16250 ForgetMem( &inSubtest->serviceType );
16251 ForgetMem( &inSubtest->serviceFQDN );
16252 ForgetMem( &inSubtest->txtPtr );
16253 ForgetCF( &inSubtest->unexpected );
16254 #if( TARGET_OS_WATCH )
16255 ForgetCF( &inSubtest->ignored );
16256 #endif
16257 _MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
16258 ForgetMem( &inSubtest->description );
16259 free( inSubtest );
16260 }
16261
16262 //===========================================================================================================================
16263
16264 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest )
16265 {
16266 DNSServiceForget( &inSubtest->registration );
16267 DNSServiceForget( &inSubtest->browse );
16268 DNSServiceForget( &inSubtest->querySRV );
16269 DNSServiceForget( &inSubtest->queryTXT );
16270 DNSServiceForget( &inSubtest->connection );
16271 }
16272
16273 //===========================================================================================================================
16274
16275 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
16276 {
16277 OSStatus err;
16278 RegistrationInterfaceItem * list;
16279 const MDNSInterfaceSubset subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
16280
16281 err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
16282 require_noerr( err, exit );
16283
16284 *outList = list;
16285
16286 exit:
16287 return( err );
16288 }
16289
16290 //===========================================================================================================================
16291
16292 static OSStatus
16293 _RegistrationTestCreateRandomTXTRecord(
16294 size_t inMinLen,
16295 size_t inMaxLen,
16296 uint8_t ** outTXTPtr,
16297 size_t * outTXTLen )
16298 {
16299 OSStatus err;
16300 uint8_t * ptr;
16301 const uint8_t * txtEnd;
16302 uint8_t * txtPtr = NULL;
16303 size_t txtLen;
16304
16305 require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
16306
16307 txtLen = RandomRange( inMinLen, inMaxLen );
16308 txtPtr = (uint8_t *) malloc( txtLen + 1 );
16309 require_action( txtPtr, exit, err = kNoMemoryErr );
16310
16311 _RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
16312
16313 ptr = txtPtr;
16314 txtEnd = &txtPtr[ txtLen ];
16315 while( ptr < txtEnd )
16316 {
16317 size_t maxLen, len;
16318
16319 maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
16320 len = RandomRange( 1, 255 );
16321 if( len > maxLen ) len = maxLen;
16322
16323 *ptr = (uint8_t) len;
16324 ptr += ( 1 + len );
16325 }
16326 check( ptr == txtEnd );
16327
16328 if( outTXTPtr )
16329 {
16330 *outTXTPtr = txtPtr;
16331 txtPtr = NULL;
16332 }
16333 if( outTXTLen ) *outTXTLen = txtLen;
16334 err = kNoErr;
16335
16336 exit:
16337 FreeNullSafe( txtPtr );
16338 return( err );
16339 }
16340
16341 //===========================================================================================================================
16342
16343 static void DNSSD_API
16344 _RegistrationSubtestRegisterCallback(
16345 DNSServiceRef inSDRef,
16346 DNSServiceFlags inFlags,
16347 DNSServiceErrorType inError,
16348 const char * inServiceName,
16349 const char * inServiceType,
16350 const char * inDomain,
16351 void * inContext )
16352 {
16353 OSStatus err;
16354 const NanoTime64 now = NanoTimeGetCurrent();
16355 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16356
16357 Unused( inSDRef );
16358
16359 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
16360 ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16361 _RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
16362 ( strcasecmp( inDomain, "local." ) == 0 ) )
16363 {
16364 if( !subtest->registered )
16365 {
16366 DNSServiceRef sdRef;
16367 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16368
16369 subtest->registered = true;
16370
16371 // Create shared connection.
16372
16373 check( !subtest->connection );
16374 err = DNSServiceCreateConnection( &subtest->connection );
16375 require_noerr( err, exit );
16376
16377 err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
16378 require_noerr( err, exit );
16379
16380 // Start browse.
16381
16382 check( !subtest->browse );
16383 sdRef = subtest->connection;
16384 err = DNSServiceBrowse( &sdRef, flags,
16385 subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
16386 subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
16387 require_noerr( err, exit );
16388
16389 subtest->browse = sdRef;
16390 }
16391 }
16392 else
16393 {
16394 char timestamp[ 32 ];
16395
16396 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
16397 "{"
16398 "%kO=%O" // resultType
16399 "%kO=%s" // timestamp
16400 "%kO=%lli" // flags
16401 "%kO=%lli" // error
16402 "%kO=%s" // serviceName
16403 "%kO=%s" // serviceType
16404 "%kO=%s" // domain
16405 "}",
16406 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Registration,
16407 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16408 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16409 kRegistrationTestReportKey_Error, (int64_t) inError,
16410 kRegistrationTestReportKey_ServiceName, inServiceName,
16411 kRegistrationTestReportKey_ServiceType, inServiceType,
16412 kRegistrationTestReportKey_Domain, inDomain );
16413 require_noerr( err, exit );
16414 }
16415 err = kNoErr;
16416
16417 exit:
16418 if( err ) _RegistrationTestExit( subtest->test, err );
16419 }
16420
16421 //===========================================================================================================================
16422
16423 static void DNSSD_API
16424 _RegistrationSubtestBrowseCallback(
16425 DNSServiceRef inSDRef,
16426 DNSServiceFlags inFlags,
16427 uint32_t inIfIndex,
16428 DNSServiceErrorType inError,
16429 const char * inServiceName,
16430 const char * inServiceType,
16431 const char * inDomain,
16432 void * inContext )
16433 {
16434 OSStatus err;
16435 NanoTime64 now;
16436 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16437 Boolean serviceIsCorrect, resultIsExpected;
16438
16439 Unused( inSDRef );
16440
16441 now = NanoTimeGetCurrent();
16442 if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16443 _RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
16444 {
16445 serviceIsCorrect = true;
16446 }
16447 else
16448 {
16449 serviceIsCorrect = false;
16450 }
16451
16452 resultIsExpected = false;
16453 if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
16454 {
16455 RegistrationResultTimes * times;
16456
16457 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
16458 if( times )
16459 {
16460 DNSServiceRef sdRef;
16461 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16462 uint32_t ifIndex;
16463
16464 resultIsExpected = true;
16465 if( !times->browseResultTime ) times->browseResultTime = now;
16466
16467 ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
16468 if( !subtest->querySRV )
16469 {
16470 // Start SRV record query.
16471
16472 sdRef = subtest->connection;
16473 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
16474 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16475 require_noerr( err, exit );
16476
16477 subtest->querySRV = sdRef;
16478 }
16479 if( !subtest->queryTXT )
16480 {
16481 // Start TXT record query.
16482
16483 sdRef = subtest->connection;
16484 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
16485 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16486 require_noerr( err, exit );
16487
16488 subtest->queryTXT = sdRef;
16489 }
16490 }
16491 }
16492
16493 if( !resultIsExpected )
16494 {
16495 CFMutableArrayRef resultArray;
16496 char timestamp[ 32 ];
16497 const char * ifNamePtr;
16498 char ifNameBuf[ IF_NAMESIZE + 1 ];
16499
16500 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16501 resultArray = subtest->unexpected;
16502 #if( TARGET_OS_WATCH )
16503 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16504 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
16505 {
16506 resultArray = subtest->ignored;
16507 }
16508 #endif
16509 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
16510 "{"
16511 "%kO=%O" // resultType
16512 "%kO=%s" // timestamp
16513 "%kO=%lli" // flags
16514 "%kO=%lli" // ifIndex
16515 "%kO=%s" // ifName
16516 "%kO=%lli" // error
16517 "%kO=%s" // serviceName
16518 "%kO=%s" // serviceType
16519 "%kO=%s" // domain
16520 "}",
16521 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Browse,
16522 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16523 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16524 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16525 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16526 kRegistrationTestReportKey_Error, (int64_t) inError,
16527 kRegistrationTestReportKey_ServiceName, inServiceName,
16528 kRegistrationTestReportKey_ServiceType, inServiceType,
16529 kRegistrationTestReportKey_Domain, inDomain );
16530 require_noerr( err, exit );
16531 }
16532 err = kNoErr;
16533
16534 exit:
16535 if( err ) _RegistrationTestExit( subtest->test, err );
16536 }
16537
16538 //===========================================================================================================================
16539
16540 static Boolean
16541 _RegistrationSubtestIsSRVRecordDataValid(
16542 RegistrationSubtest * inSubtest,
16543 const uint8_t * inRDataPtr,
16544 size_t inRDataLen,
16545 Boolean inExpectRandHostname );
16546
16547 static void DNSSD_API
16548 _RegistrationSubtestQueryCallback(
16549 DNSServiceRef inSDRef,
16550 DNSServiceFlags inFlags,
16551 uint32_t inIfIndex,
16552 DNSServiceErrorType inError,
16553 const char * inName,
16554 uint16_t inType,
16555 uint16_t inClass,
16556 uint16_t inRDataLen,
16557 const void * inRDataPtr,
16558 uint32_t inTTL,
16559 void * inContext )
16560 {
16561 OSStatus err;
16562 NanoTime64 now;
16563 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16564 Boolean resultIsExpected;
16565
16566 Unused( inSDRef );
16567 Unused( inTTL );
16568
16569 now = NanoTimeGetCurrent();
16570 resultIsExpected = false;
16571 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
16572 ( inClass == kDNSServiceClass_IN ) )
16573 {
16574 RegistrationResultTimes * times;
16575 Boolean isAWDL;
16576
16577 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
16578 if( times )
16579 {
16580 if( inType == kDNSServiceType_SRV )
16581 {
16582 Boolean expectRandHostname;
16583
16584 if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
16585 {
16586 expectRandHostname = true;
16587 }
16588 else
16589 {
16590 expectRandHostname = false;
16591 }
16592 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16593 {
16594 resultIsExpected = true;
16595 if( !times->querySRVResultTime ) times->querySRVResultTime = now;
16596 }
16597 }
16598 else if( inType == kDNSServiceType_TXT )
16599 {
16600 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16601 {
16602 resultIsExpected = true;
16603 if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
16604 }
16605 }
16606 }
16607 }
16608
16609 if( !resultIsExpected )
16610 {
16611 CFMutableArrayRef resultArray;
16612 CFMutableDictionaryRef resultDict;
16613 CFStringRef rdataKey;
16614 char * rdataStr;
16615 const char * ifNamePtr;
16616 char timestamp[ 32 ];
16617 char ifNameBuf[ IF_NAMESIZE + 1 ];
16618
16619 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16620 resultArray = subtest->unexpected;
16621 #if( TARGET_OS_WATCH )
16622 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16623 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
16624 ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
16625 {
16626 if( inType == kDNSServiceType_SRV )
16627 {
16628 const Boolean expectRandHostname = subtest->includeAWDL ? true : false;
16629
16630 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16631 {
16632 resultArray = subtest->ignored;
16633 }
16634 }
16635 else if( inType == kDNSServiceType_TXT )
16636 {
16637 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16638 {
16639 resultArray = subtest->ignored;
16640 }
16641 }
16642 }
16643 #endif
16644 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
16645 "{"
16646 "%kO=%O" // resultType
16647 "%kO=%s" // timestamp
16648 "%kO=%lli" // flags
16649 "%kO=%lli" // ifIndex
16650 "%kO=%s" // ifName
16651 "%kO=%lli" // error
16652 "%kO=%s" // serviceFQDN
16653 "%kO=%lli" // recordType
16654 "%kO=%lli" // class
16655 "}",
16656 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Query,
16657 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16658 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16659 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16660 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16661 kRegistrationTestReportKey_Error, (int64_t) inError,
16662 kRegistrationTestReportKey_ServiceFQDN, inName,
16663 kRegistrationTestReportKey_RecordType, (int64_t) inType,
16664 kRegistrationTestReportKey_RecordClass, (int64_t) inClass );
16665 require_noerr( err, exit );
16666
16667 rdataStr = NULL;
16668 DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
16669 if( rdataStr )
16670 {
16671 rdataKey = kRegistrationTestReportKey_RDataFormatted;
16672 }
16673 else
16674 {
16675 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16676 require_action( rdataStr, exit, err = kNoMemoryErr );
16677
16678 rdataKey = kRegistrationTestReportKey_RDataHexString;
16679 }
16680 err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
16681 ForgetMem( &rdataStr );
16682 if( err ) CFRelease( resultDict );
16683 require_noerr( err, exit );
16684
16685 CFArrayAppendValue( resultArray, resultDict );
16686 CFRelease( resultDict );
16687 }
16688 err = kNoErr;
16689
16690 exit:
16691 if( err ) _RegistrationTestExit( subtest->test, err );
16692 }
16693
16694 static Boolean
16695 _RegistrationSubtestIsSRVRecordDataValid(
16696 RegistrationSubtest * inSubtest,
16697 const uint8_t * inRDataPtr,
16698 size_t inRDataLen,
16699 Boolean inExpectRandHostname )
16700 {
16701 const dns_fixed_fields_srv * fields;
16702 const uint8_t * const end = &inRDataPtr[ inRDataLen ];
16703 const uint8_t * label;
16704 size_t len;
16705 uint16_t port;
16706 Boolean isValid;
16707
16708 isValid = false;
16709 require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
16710
16711 fields = (const dns_fixed_fields_srv *) inRDataPtr;
16712 port = dns_fixed_fields_srv_get_port( fields );
16713 require_quiet( port == inSubtest->port, exit );
16714
16715 // First target label should be a UUID string for the AWDL interface.
16716
16717 label = (const uint8_t *) &fields[ 1 ];
16718 require_quiet( ( end - label ) >= 1, exit );
16719
16720 len = label[ 0 ];
16721 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16722
16723 if( inExpectRandHostname )
16724 {
16725 if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
16726 }
16727 else
16728 {
16729 if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
16730 }
16731
16732 // Second target label should be "local".
16733
16734 label = &label[ 1 + len ];
16735 require_quiet( ( end - label ) >= 1, exit );
16736
16737 len = label[ 0 ];
16738 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16739
16740 if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
16741
16742 // Third target label should be the root label.
16743
16744 label = &label[ 1 + len ];
16745 require_quiet( ( end - label ) >= 1, exit );
16746
16747 len = label[ 0 ];
16748 if( len != 0 ) goto exit;
16749
16750 isValid = true;
16751
16752 exit:
16753 return( isValid );
16754 }
16755
16756 //===========================================================================================================================
16757
16758 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
16759 {
16760 if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
16761 {
16762 const char * const ptr = &inServiceType[ inSubtest->serviceTypeLen ];
16763
16764 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
16765 }
16766 return( false );
16767 }
16768
16769 //===========================================================================================================================
16770
16771 static RegistrationResultTimes *
16772 _RegistrationSubtestGetInterfaceResultTimes(
16773 RegistrationSubtest * inSubtest,
16774 uint32_t inIfIndex,
16775 Boolean * outIsAWDL )
16776 {
16777 if( inSubtest->ifList )
16778 {
16779 RegistrationInterfaceItem * item;
16780
16781 for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16782 {
16783 if( inIfIndex == item->base.ifIndex )
16784 {
16785 if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
16786 return( &item->times );
16787 }
16788 }
16789 }
16790 else
16791 {
16792 if( inIfIndex == inSubtest->ifIndex )
16793 {
16794 if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
16795 return( &inSubtest->ifTimes );
16796 }
16797 }
16798 return( NULL );
16799 }
16800
16801 //===========================================================================================================================
16802
16803 static void _RegistrationTestTimerHandler( void *inContext )
16804 {
16805 RegistrationTest * const test = (RegistrationTest *) inContext;
16806
16807 dispatch_source_forget( &test->timer );
16808 _RegistrationTestProceed( test );
16809 }
16810
16811 //===========================================================================================================================
16812
16813 #if( TARGET_OS_WATCH )
16814 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName )
16815 {
16816 NetTransportType type = kNetTransportType_Undefined;
16817
16818 SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
16819 return( ( type == kNetTransportType_WiFi ) ? true : false );
16820 }
16821 #endif
16822
16823 //===========================================================================================================================
16824 // SSDPDiscoverCmd
16825 //===========================================================================================================================
16826
16827 #define kSSDPPort 1900
16828
16829 typedef struct
16830 {
16831 HTTPHeader header; // HTTP header object for sending and receiving.
16832 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
16833 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
16834 int receiveSecs; // After send, the amount of time to spend receiving.
16835 uint32_t ifindex; // Index of the interface over which to send the query.
16836 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
16837 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
16838
16839 } SSDPDiscoverContext;
16840
16841 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
16842 static void SSDPDiscoverReadHandler( void *inContext );
16843 static int SocketToPortNumber( SocketRef inSock );
16844 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
16845
16846 static void SSDPDiscoverCmd( void )
16847 {
16848 OSStatus err;
16849 struct timeval now;
16850 SSDPDiscoverContext * context;
16851 dispatch_source_t signalSource = NULL;
16852 SocketRef sockV4 = kInvalidSocketRef;
16853 SocketRef sockV6 = kInvalidSocketRef;
16854 ssize_t n;
16855 int sendCount;
16856
16857 // Set up SIGINT handler.
16858
16859 signal( SIGINT, SIG_IGN );
16860 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
16861 require_noerr( err, exit );
16862 dispatch_resume( signalSource );
16863
16864 // Check command parameters.
16865
16866 if( gSSDPDiscover_ReceiveSecs < -1 )
16867 {
16868 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
16869 err = kParamErr;
16870 goto exit;
16871 }
16872
16873 // Create context.
16874
16875 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
16876 require_action( context, exit, err = kNoMemoryErr );
16877
16878 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
16879 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
16880 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
16881
16882 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
16883 require_noerr_quiet( err, exit );
16884
16885 // Set up IPv4 socket.
16886
16887 if( context->useIPv4 )
16888 {
16889 int port;
16890 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
16891 require_noerr( err, exit );
16892
16893 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
16894 require_noerr( err, exit );
16895
16896 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
16897 err = map_socket_noerr_errno( sockV4, err );
16898 require_noerr( err, exit );
16899 }
16900
16901 // Set up IPv6 socket.
16902
16903 if( context->useIPv6 )
16904 {
16905 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
16906 require_noerr( err, exit );
16907
16908 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
16909 require_noerr( err, exit );
16910
16911 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
16912 err = map_socket_noerr_errno( sockV6, err );
16913 require_noerr( err, exit );
16914 }
16915
16916 // Print prologue.
16917
16918 SSDPDiscoverPrintPrologue( context );
16919
16920 // Send mDNS query message.
16921
16922 sendCount = 0;
16923 if( IsValidSocket( sockV4 ) )
16924 {
16925 struct sockaddr_in mcastAddr4;
16926
16927 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
16928 SIN_LEN_SET( &mcastAddr4 );
16929 mcastAddr4.sin_family = AF_INET;
16930 mcastAddr4.sin_port = htons( kSSDPPort );
16931 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
16932
16933 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
16934 require_noerr( err, exit );
16935
16936 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
16937 (socklen_t) sizeof( mcastAddr4 ) );
16938 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
16939 if( err )
16940 {
16941 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
16942 ForgetSocket( &sockV4 );
16943 }
16944 else
16945 {
16946 if( gSSDPDiscover_Verbose )
16947 {
16948 gettimeofday( &now, NULL );
16949 FPrintF( stdout, "---\n" );
16950 FPrintF( stdout, "Send time: %{du:time}\n", &now );
16951 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
16952 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
16953 FPrintF( stdout, "Message size: %zu\n", context->header.len );
16954 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
16955 }
16956 ++sendCount;
16957 }
16958 }
16959
16960 if( IsValidSocket( sockV6 ) )
16961 {
16962 struct sockaddr_in6 mcastAddr6;
16963
16964 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
16965 SIN6_LEN_SET( &mcastAddr6 );
16966 mcastAddr6.sin6_family = AF_INET6;
16967 mcastAddr6.sin6_port = htons( kSSDPPort );
16968 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16969 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
16970 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
16971
16972 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
16973 require_noerr( err, exit );
16974
16975 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
16976 (socklen_t) sizeof( mcastAddr6 ) );
16977 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
16978 if( err )
16979 {
16980 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
16981 ForgetSocket( &sockV6 );
16982 }
16983 else
16984 {
16985 if( gSSDPDiscover_Verbose )
16986 {
16987 gettimeofday( &now, NULL );
16988 FPrintF( stdout, "---\n" );
16989 FPrintF( stdout, "Send time: %{du:time}\n", &now );
16990 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
16991 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
16992 FPrintF( stdout, "Message size: %zu\n", context->header.len );
16993 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
16994 }
16995 ++sendCount;
16996 }
16997 }
16998 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
16999
17000 // If there's no wait period after the send, then exit.
17001
17002 if( context->receiveSecs == 0 ) goto exit;
17003
17004 // Create dispatch read sources for socket(s).
17005
17006 if( IsValidSocket( sockV4 ) )
17007 {
17008 SocketContext * sockCtx;
17009
17010 err = SocketContextCreate( sockV4, context, &sockCtx );
17011 require_noerr( err, exit );
17012 sockV4 = kInvalidSocketRef;
17013
17014 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17015 &context->readSourceV4 );
17016 if( err ) ForgetSocketContext( &sockCtx );
17017 require_noerr( err, exit );
17018
17019 dispatch_resume( context->readSourceV4 );
17020 }
17021
17022 if( IsValidSocket( sockV6 ) )
17023 {
17024 SocketContext * sockCtx;
17025
17026 err = SocketContextCreate( sockV6, context, &sockCtx );
17027 require_noerr( err, exit );
17028 sockV6 = kInvalidSocketRef;
17029
17030 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17031 &context->readSourceV6 );
17032 if( err ) ForgetSocketContext( &sockCtx );
17033 require_noerr( err, exit );
17034
17035 dispatch_resume( context->readSourceV6 );
17036 }
17037
17038 if( context->receiveSecs > 0 )
17039 {
17040 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
17041 Exit );
17042 }
17043 dispatch_main();
17044
17045 exit:
17046 ForgetSocket( &sockV4 );
17047 ForgetSocket( &sockV6 );
17048 dispatch_source_forget( &signalSource );
17049 exit( err ? 1 : 0 );
17050 }
17051
17052 static int SocketToPortNumber( SocketRef inSock )
17053 {
17054 OSStatus err;
17055 sockaddr_ip sip;
17056 socklen_t len;
17057
17058 len = (socklen_t) sizeof( sip );
17059 err = getsockname( inSock, &sip.sa, &len );
17060 err = map_socket_noerr_errno( inSock, err );
17061 check_noerr( err );
17062 return( err ? -1 : SockAddrGetPort( &sip ) );
17063 }
17064
17065 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
17066 {
17067 OSStatus err;
17068
17069 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
17070 require_noerr( err, exit );
17071
17072 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
17073 require_noerr( err, exit );
17074
17075 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
17076 require_noerr( err, exit );
17077
17078 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
17079 require_noerr( err, exit );
17080
17081 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
17082 require_noerr( err, exit );
17083
17084 err = HTTPHeader_Commit( inHeader );
17085 require_noerr( err, exit );
17086
17087 exit:
17088 return( err );
17089 }
17090
17091 //===========================================================================================================================
17092 // SSDPDiscoverPrintPrologue
17093 //===========================================================================================================================
17094
17095 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
17096 {
17097 const int receiveSecs = inContext->receiveSecs;
17098 const char * ifName;
17099 char ifNameBuf[ IF_NAMESIZE + 1 ];
17100 NetTransportType ifType;
17101
17102 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
17103
17104 ifType = kNetTransportType_Undefined;
17105 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
17106
17107 FPrintF( stdout, "Interface: %s/%d/%s\n",
17108 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
17109 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
17110 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
17111 FPrintF( stdout, "Receive duration: " );
17112 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
17113 else FPrintF( stdout, "∞\n" );
17114 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17115 }
17116
17117 //===========================================================================================================================
17118 // SSDPDiscoverReadHandler
17119 //===========================================================================================================================
17120
17121 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader );
17122
17123 static void SSDPDiscoverReadHandler( void *inContext )
17124 {
17125 OSStatus err;
17126 struct timeval now;
17127 SocketContext * const sockCtx = (SocketContext *) inContext;
17128 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
17129 HTTPHeader * const header = &context->header;
17130 sockaddr_ip fromAddr;
17131 size_t msgLen;
17132
17133 gettimeofday( &now, NULL );
17134
17135 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
17136 NULL, NULL, NULL, NULL );
17137 require_noerr( err, exit );
17138
17139 FPrintF( stdout, "---\n" );
17140 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
17141 FPrintF( stdout, "Source: %##a\n", &fromAddr );
17142 FPrintF( stdout, "Message size: %zu\n", msgLen );
17143 header->len = msgLen;
17144 if( _HTTPHeader_Validate( header ) )
17145 {
17146 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
17147 if( header->extraDataLen > 0 )
17148 {
17149 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
17150 }
17151 }
17152 else
17153 {
17154 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
17155 goto exit;
17156 }
17157
17158 exit:
17159 if( err ) exit( 1 );
17160 }
17161
17162 //===========================================================================================================================
17163 // _HTTPHeader_Validate
17164 //
17165 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17166 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17167 //
17168 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17169 //===========================================================================================================================
17170
17171 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader )
17172 {
17173 const char * src;
17174 const char * end;
17175
17176 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17177
17178 require( inHeader->len < sizeof( inHeader->buf ), exit );
17179 src = inHeader->buf;
17180 end = src + inHeader->len;
17181 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
17182 {
17183 src += 4;
17184 }
17185 else
17186 {
17187 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17188 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17189
17190 for( ;; )
17191 {
17192 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
17193 if( src >= end ) goto exit;
17194 ++src;
17195 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17196 {
17197 src += 2;
17198 break;
17199 }
17200 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
17201 {
17202 src += 1;
17203 break;
17204 }
17205 }
17206 }
17207 inHeader->extraDataPtr = src;
17208 inHeader->extraDataLen = (size_t)( end - src );
17209 inHeader->len = (size_t)( src - inHeader->buf );
17210 return( true );
17211
17212 exit:
17213 return( false );
17214 }
17215
17216 #if( TARGET_OS_DARWIN )
17217 //===========================================================================================================================
17218 // ResQueryCmd
17219 //===========================================================================================================================
17220
17221 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17222
17223 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
17224 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
17225 int,
17226 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17227 ( dname, class, type, answer, anslen ) );
17228
17229 // res_query() from libinfo
17230
17231 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
17232 SOFT_LINK_FUNCTION_EX( info, res_query,
17233 int,
17234 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17235 ( dname, class, type, answer, anslen ) );
17236
17237 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
17238
17239 static void ResQueryCmd( void )
17240 {
17241 OSStatus err;
17242 res_query_f res_query_ptr;
17243 int n;
17244 uint16_t type, class;
17245 uint8_t answer[ 1024 ];
17246
17247 // Get pointer to one of the res_query() functions.
17248
17249 if( gResQuery_UseLibInfo )
17250 {
17251 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
17252 {
17253 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
17254 err = kNotFoundErr;
17255 goto exit;
17256 }
17257 res_query_ptr = soft_res_query;
17258 }
17259 else
17260 {
17261 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
17262 {
17263 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
17264 err = kNotFoundErr;
17265 goto exit;
17266 }
17267 res_query_ptr = soft_res_9_query;
17268 }
17269
17270 // Get record type.
17271
17272 err = RecordTypeFromArgString( gResQuery_Type, &type );
17273 require_noerr( err, exit );
17274
17275 // Get record class.
17276
17277 if( gResQuery_Class )
17278 {
17279 err = RecordClassFromArgString( gResQuery_Class, &class );
17280 require_noerr( err, exit );
17281 }
17282 else
17283 {
17284 class = kDNSServiceClass_IN;
17285 }
17286
17287 // Print prologue.
17288
17289 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
17290 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17291 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17292 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17293 FPrintF( stdout, "---\n" );
17294
17295 // Call res_query().
17296
17297 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
17298 if( n < 0 )
17299 {
17300 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17301 err = kUnknownErr;
17302 goto exit;
17303 }
17304
17305 // Print result.
17306
17307 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17308
17309 exit:
17310 if( err ) exit( 1 );
17311 }
17312
17313 //===========================================================================================================================
17314 // ResolvDNSQueryCmd
17315 //===========================================================================================================================
17316
17317 // 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
17318 // avoid including the header file.
17319
17320 typedef void * dns_handle_t;
17321
17322 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
17323 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
17324 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
17325 int32_t, (
17326 dns_handle_t dns,
17327 const char * name,
17328 uint32_t dnsclass,
17329 uint32_t dnstype,
17330 char * buf,
17331 uint32_t len,
17332 struct sockaddr * from,
17333 uint32_t * fromlen ),
17334 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
17335
17336 static void ResolvDNSQueryCmd( void )
17337 {
17338 OSStatus err;
17339 int n;
17340 dns_handle_t dns = NULL;
17341 uint16_t type, class;
17342 sockaddr_ip from;
17343 uint32_t fromLen;
17344 uint8_t answer[ 1024 ];
17345
17346 // Make sure that the required symbols are available.
17347
17348 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
17349 {
17350 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
17351 err = kNotFoundErr;
17352 goto exit;
17353 }
17354
17355 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
17356 {
17357 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
17358 err = kNotFoundErr;
17359 goto exit;
17360 }
17361
17362 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
17363 {
17364 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
17365 err = kNotFoundErr;
17366 goto exit;
17367 }
17368
17369 // Get record type.
17370
17371 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
17372 require_noerr( err, exit );
17373
17374 // Get record class.
17375
17376 if( gResolvDNSQuery_Class )
17377 {
17378 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
17379 require_noerr( err, exit );
17380 }
17381 else
17382 {
17383 class = kDNSServiceClass_IN;
17384 }
17385
17386 // Get dns handle.
17387
17388 dns = soft_dns_open( gResolvDNSQuery_Path );
17389 if( !dns )
17390 {
17391 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
17392 err = kUnknownErr;
17393 goto exit;
17394 }
17395
17396 // Print prologue.
17397
17398 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
17399 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17400 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17401 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
17402 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17403 FPrintF( stdout, "---\n" );
17404
17405 // Call dns_query().
17406
17407 memset( &from, 0, sizeof( from ) );
17408 fromLen = (uint32_t) sizeof( from );
17409 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
17410 &fromLen );
17411 if( n < 0 )
17412 {
17413 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17414 err = kUnknownErr;
17415 goto exit;
17416 }
17417
17418 // Print result.
17419
17420 FPrintF( stdout, "From: %##a\n", &from );
17421 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17422
17423 exit:
17424 if( dns ) soft_dns_free( dns );
17425 if( err ) exit( 1 );
17426 }
17427
17428 //===========================================================================================================================
17429 // CFHostCmd
17430 //===========================================================================================================================
17431
17432 static void
17433 _CFHostResolveCallback(
17434 CFHostRef inHost,
17435 CFHostInfoType inInfoType,
17436 const CFStreamError * inError,
17437 void * inInfo );
17438
17439 static void CFHostCmd( void )
17440 {
17441 OSStatus err;
17442 CFStringRef name;
17443 Boolean success;
17444 CFHostRef host = NULL;
17445 CFHostClientContext context;
17446 CFStreamError streamErr;
17447
17448 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
17449 require_action( name, exit, err = kUnknownErr );
17450
17451 host = CFHostCreateWithName( kCFAllocatorDefault, name );
17452 ForgetCF( &name );
17453 require_action( host, exit, err = kUnknownErr );
17454
17455 memset( &context, 0, sizeof( context ) );
17456 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
17457 require_action( success, exit, err = kUnknownErr );
17458
17459 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
17460
17461 // Print prologue.
17462
17463 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
17464 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17465 FPrintF( stdout, "---\n" );
17466
17467 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
17468 require_action( success, exit, err = kUnknownErr );
17469 err = kNoErr;
17470
17471 CFRunLoopRun();
17472
17473 exit:
17474 CFReleaseNullSafe( host );
17475 if( err ) exit( 1 );
17476 }
17477
17478 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
17479 {
17480 OSStatus err;
17481 struct timeval now;
17482
17483 gettimeofday( &now, NULL );
17484
17485 Unused( inInfoType );
17486 Unused( inInfo );
17487
17488 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
17489 {
17490 err = inError->error;
17491 if( inError->domain == kCFStreamErrorDomainNetDB )
17492 {
17493 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
17494 }
17495 else
17496 {
17497 FPrintF( stderr, "Error %#m\n", err );
17498 }
17499 }
17500 else
17501 {
17502 CFArrayRef addresses;
17503 CFIndex count, i;
17504 CFDataRef addrData;
17505 const struct sockaddr * sockAddr;
17506 Boolean wasResolved = false;
17507
17508 addresses = CFHostGetAddressing( inHost, &wasResolved );
17509 check( wasResolved );
17510
17511 if( addresses )
17512 {
17513 count = CFArrayGetCount( addresses );
17514 for( i = 0; i < count; ++i )
17515 {
17516 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
17517 require_noerr( err, exit );
17518
17519 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
17520 FPrintF( stdout, "%##a\n", sockAddr );
17521 }
17522 }
17523 err = kNoErr;
17524 }
17525
17526 FPrintF( stdout, "---\n" );
17527 FPrintF( stdout, "End time: %{du:time}\n", &now );
17528
17529 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
17530
17531 exit:
17532 exit( err ? 1 : 0 );
17533 }
17534
17535 //===========================================================================================================================
17536 // DNSConfigAddCmd
17537 //
17538 // Note: Based on ajn's supplemental test tool.
17539 //===========================================================================================================================
17540
17541 static void DNSConfigAddCmd( void )
17542 {
17543 OSStatus err;
17544 CFMutableDictionaryRef dict = NULL;
17545 CFMutableArrayRef array = NULL;
17546 size_t i;
17547 SCDynamicStoreRef store = NULL;
17548 CFStringRef key = NULL;
17549 Boolean success;
17550
17551 // Create dictionary.
17552
17553 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
17554 require_action( dict, exit, err = kNoMemoryErr );
17555
17556 // Add DNS server IP addresses.
17557
17558 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
17559 require_action( array, exit, err = kNoMemoryErr );
17560
17561 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
17562 {
17563 CFStringRef addrStr;
17564
17565 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
17566 require_action( addrStr, exit, err = kUnknownErr );
17567
17568 CFArrayAppendValue( array, addrStr );
17569 CFRelease( addrStr );
17570 }
17571
17572 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
17573 ForgetCF( &array );
17574
17575 // Add domains, if any.
17576
17577 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
17578 require_action( array, exit, err = kNoMemoryErr );
17579
17580 if( gDNSConfigAdd_DomainCount > 0 )
17581 {
17582 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
17583 {
17584 CFStringRef domainStr;
17585
17586 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
17587 require_action( domainStr, exit, err = kUnknownErr );
17588
17589 CFArrayAppendValue( array, domainStr );
17590 CFRelease( domainStr );
17591 }
17592 }
17593 else
17594 {
17595 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17596
17597 CFArrayAppendValue( array, CFSTR( "" ) );
17598 }
17599
17600 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
17601 ForgetCF( &array );
17602
17603 // Add interface, if any.
17604
17605 if( gDNSConfigAdd_Interface )
17606 {
17607 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
17608 require_noerr( err, exit );
17609
17610 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
17611 }
17612
17613 // Set dictionary in dynamic store.
17614
17615 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17616 err = map_scerror( store );
17617 require_noerr( err, exit );
17618
17619 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
17620 require_action( key, exit, err = kUnknownErr );
17621
17622 success = SCDynamicStoreSetValue( store, key, dict );
17623 require_action( success, exit, err = kUnknownErr );
17624
17625 exit:
17626 CFReleaseNullSafe( dict );
17627 CFReleaseNullSafe( array );
17628 CFReleaseNullSafe( store );
17629 CFReleaseNullSafe( key );
17630 gExitCode = err ? 1 : 0;
17631 }
17632
17633 //===========================================================================================================================
17634 // DNSConfigRemoveCmd
17635 //===========================================================================================================================
17636
17637 static void DNSConfigRemoveCmd( void )
17638 {
17639 OSStatus err;
17640 SCDynamicStoreRef store = NULL;
17641 CFStringRef key = NULL;
17642 Boolean success;
17643
17644 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17645 err = map_scerror( store );
17646 require_noerr( err, exit );
17647
17648 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
17649 require_action( key, exit, err = kUnknownErr );
17650
17651 success = SCDynamicStoreRemoveValue( store, key );
17652 require_action( success, exit, err = kUnknownErr );
17653
17654 exit:
17655 CFReleaseNullSafe( store );
17656 CFReleaseNullSafe( key );
17657 gExitCode = err ? 1 : 0;
17658 }
17659
17660 //===========================================================================================================================
17661 // XPCSendCmd
17662 //===========================================================================================================================
17663
17664 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
17665
17666 static void XPCSendCmd( void )
17667 {
17668 OSStatus err;
17669 xpc_object_t msg, reply;
17670
17671 err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
17672 require_noerr_quiet( err, exit );
17673
17674 FPrintF( stdout, "Service: %s\n", gXPCSend_ServiceName );
17675 FPrintF( stdout, "Message: %s\n", gXPCSend_MessageStr );
17676 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17677 FPrintF( stdout, "---\n" );
17678 FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
17679
17680 err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
17681 xpc_forget( &msg );
17682 require_noerr_quiet( err, exit );
17683
17684 FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
17685 FPrintF( stdout, "---\n" );
17686 FPrintF( stdout, "End time: %{du:time}\n", NULL );
17687 xpc_forget( &reply );
17688
17689 exit:
17690 if( err ) ErrQuit( 1, "error: %#m\n", err );
17691 }
17692
17693 //===========================================================================================================================
17694 // _XPCDictionaryCreateFromString
17695 //===========================================================================================================================
17696
17697 #define kXPCObjectPrefix_Bool "bool:"
17698 #define kXPCObjectPrefix_Data "data:"
17699 #define kXPCObjectPrefix_Int64 "int:"
17700 #define kXPCObjectPrefix_String "string:"
17701 #define kXPCObjectPrefix_UInt64 "uint:"
17702 #define kXPCObjectPrefix_UUID "uuid:"
17703
17704 typedef struct XPCListItem XPCListItem;
17705 struct XPCListItem
17706 {
17707 XPCListItem * next;
17708 xpc_object_t obj;
17709 char * key;
17710 };
17711
17712 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
17713 static void _XPCListItemFree( XPCListItem *inItem );
17714 static void _XPCListFree( XPCListItem *inList );
17715
17716 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject );
17717
17718 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
17719 {
17720 OSStatus err;
17721 xpc_object_t container;
17722 const char * ptr = inString;
17723 const char * const end = inString + strlen( inString );
17724 XPCListItem * list = NULL;
17725
17726 container = xpc_dictionary_create( NULL, NULL, 0 );
17727 require_action( container, exit, err = kNoMemoryErr );
17728
17729 while( *ptr )
17730 {
17731 xpc_type_t containerType;
17732 xpc_object_t value;
17733 int c;
17734 char keyStr[ 256 ];
17735 char valStr[ 256 ];
17736
17737 // At this point, zero or more of the current container's elements have been parsed.
17738 // Skip the white space leading up to the container's next element, if any, or the container's end.
17739
17740 while( isspace_safe( *ptr ) ) ++ptr;
17741
17742 // Check if we're done with the current container.
17743
17744 c = *ptr;
17745 if( c == '\0' ) break;
17746
17747 containerType = xpc_get_type( container );
17748 if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
17749 ( ( containerType == XPC_TYPE_ARRAY ) && ( c == ']' ) ) )
17750 {
17751 XPCListItem * item;
17752
17753 item = list;
17754 require_action_quiet( item, exit, err = kMalformedErr );
17755
17756 ++ptr;
17757
17758 // Add the current container to its parent container.
17759
17760 if( item->key )
17761 {
17762 xpc_dictionary_set_value( item->obj, item->key, container );
17763 }
17764 else
17765 {
17766 xpc_array_append_value( item->obj, container );
17767 }
17768
17769 // Continue with the parent container.
17770
17771 xpc_release( container );
17772 container = xpc_retain( item->obj );
17773 list = item->next;
17774 _XPCListItemFree( item );
17775 continue;
17776 }
17777
17778 // If the current container is a dictionary, parse the key string.
17779
17780 if( containerType == XPC_TYPE_DICTIONARY )
17781 {
17782 err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
17783 require_noerr_quiet( err, exit );
17784
17785 c = *ptr;
17786 require_action_quiet( c == '=', exit, err = kMalformedErr );
17787 ++ptr;
17788 }
17789
17790 // Check if the value is a dictionary ({...}) or an array ([...]).
17791
17792 c = *ptr;
17793 if( ( c == '{' ) || ( c == '[' ) )
17794 {
17795 XPCListItem * item;
17796
17797 ++ptr;
17798
17799 // Save the current container.
17800
17801 err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
17802 require_noerr( err, exit );
17803
17804 item->next = list;
17805 list = item;
17806 item = NULL;
17807
17808 // Create and continue with the child container.
17809
17810 xpc_release( container );
17811 if( c == '{' )
17812 {
17813 container = xpc_dictionary_create( NULL, NULL, 0 );
17814 require_action( container, exit, err = kNoMemoryErr );
17815 }
17816 else
17817 {
17818 container = xpc_array_create( NULL, 0 );
17819 require_action( container, exit, err = kNoMemoryErr );
17820 }
17821 continue;
17822 }
17823
17824 // Parse the value string.
17825
17826 err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
17827 require_noerr_quiet( err, exit );
17828
17829 err = _XPCObjectFromString( valStr, &value );
17830 require_noerr_quiet( err, exit );
17831
17832 if( containerType == XPC_TYPE_DICTIONARY )
17833 {
17834 xpc_dictionary_set_value( container, keyStr, value );
17835 }
17836 else
17837 {
17838 xpc_array_append_value( container, value );
17839 }
17840 xpc_forget( &value );
17841 }
17842 require_action_quiet( !list, exit, err = kMalformedErr );
17843
17844 check( container );
17845 check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
17846
17847 *outDict = container;
17848 container = NULL;
17849 err = kNoErr;
17850
17851 exit:
17852 xpc_release_null_safe( container );
17853 if( list ) _XPCListFree( list );
17854 return( err );
17855 }
17856
17857 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject )
17858 {
17859 OSStatus err;
17860 xpc_object_t object;
17861
17862 if( 0 ) {}
17863
17864 // Bool
17865
17866 else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
17867 {
17868 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Bool );
17869 bool value;
17870
17871 if( IsTrueString( str, kSizeCString ) )
17872 {
17873 value = true;
17874 }
17875 else if( IsFalseString( str, kSizeCString ) )
17876 {
17877 value = false;
17878 }
17879 else
17880 {
17881 err = kValueErr;
17882 goto exit;
17883 }
17884
17885 object = xpc_bool_create( value );
17886 require_action( object, exit, err = kNoMemoryErr );
17887 }
17888
17889 // Data
17890
17891 else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
17892 {
17893 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Data );
17894 uint8_t * dataPtr;
17895 size_t dataLen;
17896
17897 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
17898 require_noerr( err, exit );
17899
17900 object = xpc_data_create( dataPtr, dataLen );
17901 free( dataPtr );
17902 require_action( object, exit, err = kNoMemoryErr );
17903 }
17904
17905 // Int64
17906
17907 else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
17908 {
17909 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
17910 int64_t i64;
17911
17912 i64 = _StringToInt64( str, &err );
17913 require_noerr_quiet( err, exit );
17914
17915 object = xpc_int64_create( i64 );
17916 require_action( object, exit, err = kNoMemoryErr );
17917 }
17918
17919 // String
17920
17921 else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
17922 {
17923 const char * const str = inString + sizeof_string( kXPCObjectPrefix_String );
17924
17925 object = xpc_string_create( str );
17926 require_action( object, exit, err = kNoMemoryErr );
17927 }
17928
17929 // UInt64
17930
17931 else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
17932 {
17933 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
17934 uint64_t u64;
17935
17936 u64 = _StringToUInt64( str, &err );
17937 require_noerr_quiet( err, exit );
17938
17939 object = xpc_uint64_create( u64 );
17940 require_action( object, exit, err = kNoMemoryErr );
17941 }
17942
17943 // UUID
17944
17945 else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
17946 {
17947 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UUID );
17948 uuid_t uuid;
17949
17950 err = uuid_parse( str, uuid );
17951 require_noerr_action_quiet( err, exit, err = kValueErr );
17952
17953 object = xpc_uuid_create( uuid );
17954 require_action( object, exit, err = kNoMemoryErr );
17955 }
17956
17957 // Unsupported prefix
17958
17959 else
17960 {
17961 err = kValueErr;
17962 goto exit;
17963 }
17964
17965 *outObject = object;
17966 err = kNoErr;
17967
17968 exit:
17969 return( err );
17970 }
17971
17972 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
17973 {
17974 OSStatus err;
17975 XPCListItem * item;
17976
17977 item = (XPCListItem *) calloc( 1, sizeof( *item ) );
17978 require_action( item, exit, err = kNoMemoryErr );
17979
17980 item->obj = xpc_retain( inObject );
17981 if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
17982 {
17983 item->key = strdup( inKey );
17984 require_action( item->key, exit, err = kNoMemoryErr );
17985 }
17986
17987 *outItem = item;
17988 item = NULL;
17989 err = kNoErr;
17990
17991 exit:
17992 if( item ) _XPCListItemFree( item );
17993 return( err );
17994 }
17995
17996 static void _XPCListItemFree( XPCListItem *inItem )
17997 {
17998 xpc_forget( &inItem->obj );
17999 ForgetMem( &inItem->key );
18000 free( inItem );
18001 }
18002
18003 static void _XPCListFree( XPCListItem *inList )
18004 {
18005 XPCListItem * item;
18006
18007 while( ( item = inList ) != NULL )
18008 {
18009 inList = item->next;
18010 _XPCListItemFree( item );
18011 }
18012 }
18013 #endif // TARGET_OS_DARWIN
18014
18015 #if( MDNSRESPONDER_PROJECT )
18016 //===========================================================================================================================
18017 // InterfaceMonitorCmd
18018 //===========================================================================================================================
18019
18020 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
18021 static void _InterfaceMonitorSignalHandler( void *inContext );
18022
18023 static void InterfaceMonitorCmd( void )
18024 {
18025 OSStatus err;
18026 mdns_interface_monitor_t monitor;
18027 dispatch_source_t signalSource = NULL;
18028 uint32_t ifIndex;
18029 __block int exitCode;
18030
18031 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
18032 require_noerr_quiet( err, exit );
18033
18034 monitor = mdns_interface_monitor_create( ifIndex );
18035 require_action( monitor, exit, err = kNoResourcesErr );
18036
18037 exitCode = 0;
18038 mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
18039 mdns_interface_monitor_set_event_handler( monitor,
18040 ^( mdns_event_t inEvent, OSStatus inError )
18041 {
18042 switch( inEvent )
18043 {
18044 case mdns_event_error:
18045 FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
18046 mdns_interface_monitor_invalidate( monitor );
18047 exitCode = 1;
18048 break;
18049
18050 case mdns_event_invalidated:
18051 FPrintF( stdout, "Interface monitor invalidated.\n" );
18052 mdns_release( monitor );
18053 exit( exitCode );
18054
18055 default:
18056 FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
18057 break;
18058 }
18059 } );
18060 mdns_interface_monitor_set_update_handler( monitor,
18061 ^( __unused mdns_interface_flags_t inChangeFlags )
18062 {
18063 _InterfaceMonitorPrint( monitor );
18064 } );
18065
18066 _InterfaceMonitorPrint( monitor );
18067 mdns_interface_monitor_activate( monitor );
18068
18069 signal( SIGINT, SIG_IGN );
18070 err = DispatchSignalSourceCreate( SIGINT, _InterfaceMonitorSignalHandler, monitor, &signalSource );
18071 require_noerr( err, exit );
18072 dispatch_resume( signalSource );
18073
18074 dispatch_main();
18075
18076 exit:
18077 if( err ) ErrQuit( 1, "error: %#m\n", err );
18078 }
18079
18080 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
18081 {
18082 FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
18083 }
18084
18085 static void _InterfaceMonitorSignalHandler( void *inContext )
18086 {
18087 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
18088 }
18089
18090 //===========================================================================================================================
18091 // DNSProxyCmd
18092 //===========================================================================================================================
18093
18094 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
18095 static void _DNSProxyCmdSignalHandler( void *inContext );
18096
18097 static void DNSProxyCmd( void )
18098 {
18099 OSStatus err;
18100 size_t i;
18101 DNSXConnRef connection;
18102 IfIndex inputIfIndexes[ MaxInputIf ];
18103 dispatch_source_t sigIntSource = NULL;
18104 dispatch_source_t sigTermSource = NULL;
18105 uint32_t outputIfIndex;
18106 char ifName[ kInterfaceNameBufLen ];
18107
18108 if( gDNSProxy_InputInterfaceCount > MaxInputIf )
18109 {
18110 FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
18111 gDNSProxy_InputInterfaceCount, MaxInputIf );
18112 err = kRangeErr;
18113 goto exit;
18114 }
18115
18116 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18117 {
18118 uint32_t ifIndex;
18119
18120 err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
18121 require_noerr_quiet( err, exit );
18122
18123 inputIfIndexes[ i ] = ifIndex;
18124 }
18125 while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0; // Remaining interface indexes are required to be 0.
18126
18127 if( gDNSProxy_OutputInterface )
18128 {
18129 err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
18130 require_noerr_quiet( err, exit );
18131 }
18132 else
18133 {
18134 outputIfIndex = kDNSIfindexAny;
18135 }
18136
18137 FPrintF( stdout, "Input Interfaces:" );
18138 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18139 {
18140 const uint32_t ifIndex = (uint32_t) inputIfIndexes[ i ];
18141
18142 FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
18143 }
18144 FPrintF( stdout, "\n" );
18145 FPrintF( stdout, "Output Interface: %u (%s)\n", outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
18146 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18147 FPrintF( stdout, "---\n" );
18148
18149 connection = NULL;
18150 err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex, dispatch_get_main_queue(),
18151 _DNSProxyCallback );
18152 require_noerr_quiet( err, exit );
18153
18154 signal( SIGINT, SIG_IGN );
18155 err = DispatchSignalSourceCreate( SIGINT, _DNSProxyCmdSignalHandler, connection, &sigIntSource );
18156 require_noerr( err, exit );
18157 dispatch_activate( sigIntSource );
18158
18159 signal( SIGTERM, SIG_IGN );
18160 err = DispatchSignalSourceCreate( SIGTERM, _DNSProxyCmdSignalHandler, connection, &sigTermSource );
18161 require_noerr( err, exit );
18162 dispatch_activate( sigTermSource );
18163
18164 dispatch_main();
18165
18166 exit:
18167 if( err ) ErrQuit( 1, "error: %#m\n", err );
18168 }
18169
18170 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
18171 {
18172 Unused( inConnection );
18173
18174 if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
18175 }
18176
18177 static void _DNSProxyCmdSignalHandler( void *inContext )
18178 {
18179 DNSXConnRef const connection = (DNSXConnRef) inContext;
18180 struct timeval now;
18181
18182 gettimeofday( &now, NULL );
18183
18184 DNSXRefDeAlloc( connection );
18185
18186 FPrintF( stdout, "---\n" );
18187 FPrintF( stdout, "End time: %{du:time}\n", &now );
18188 exit( 0 );
18189 }
18190
18191 #endif // MDNSRESPONDER_PROJECT
18192
18193 //===========================================================================================================================
18194 // DaemonVersionCmd
18195 //===========================================================================================================================
18196
18197 static void DaemonVersionCmd( void )
18198 {
18199 OSStatus err;
18200 uint32_t size, version;
18201 char strBuf[ 16 ];
18202
18203 size = (uint32_t) sizeof( version );
18204 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
18205 require_noerr( err, exit );
18206
18207 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
18208
18209 exit:
18210 if( err ) exit( 1 );
18211 }
18212
18213 //===========================================================================================================================
18214 // Exit
18215 //===========================================================================================================================
18216
18217 static void Exit( void *inContext )
18218 {
18219 const char * const reason = (const char *) inContext;
18220
18221 FPrintF( stdout, "---\n" );
18222 FPrintF( stdout, "End time: %{du:time}\n", NULL );
18223 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
18224 exit( gExitCode );
18225 }
18226
18227 //===========================================================================================================================
18228 // _PrintFExtensionTimestampHandler
18229 //===========================================================================================================================
18230
18231 static int
18232 _PrintFExtensionTimestampHandler(
18233 PrintFContext * inContext,
18234 PrintFFormat * inFormat,
18235 PrintFVAList * inArgs,
18236 void * inUserContext )
18237 {
18238 struct timeval now;
18239 const struct timeval * tv;
18240 struct tm * localTime;
18241 size_t len;
18242 int n;
18243 char dateTimeStr[ 32 ];
18244
18245 Unused( inUserContext );
18246
18247 tv = va_arg( inArgs->args, const struct timeval * );
18248 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18249
18250 if( !tv )
18251 {
18252 gettimeofday( &now, NULL );
18253 tv = &now;
18254 }
18255 localTime = localtime( &tv->tv_sec );
18256 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
18257 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
18258
18259 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
18260
18261 exit:
18262 return( n );
18263 }
18264
18265 //===========================================================================================================================
18266 // _PrintFExtensionDNSMessageHandler
18267 //===========================================================================================================================
18268
18269 static int
18270 _PrintFExtensionDNSMessageHandler(
18271 PrintFContext * inContext,
18272 PrintFFormat * inFormat,
18273 PrintFVAList * inArgs,
18274 void * inUserContext )
18275 {
18276 OSStatus err;
18277 const void * msgPtr;
18278 size_t msgLen;
18279 char * text;
18280 int n;
18281 Boolean isMDNS;
18282 Boolean printRawRData;
18283
18284 Unused( inUserContext );
18285
18286 msgPtr = va_arg( inArgs->args, const void * );
18287 msgLen = va_arg( inArgs->args, size_t );
18288 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18289
18290 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
18291 printRawRData = ( inFormat->precision > 0 ) ? true : false;
18292 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
18293 if( !err )
18294 {
18295 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
18296 free( text );
18297 }
18298 else
18299 {
18300 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
18301 }
18302
18303 exit:
18304 return( n );
18305 }
18306
18307 //===========================================================================================================================
18308 // _PrintFExtensionCallbackFlagsHandler
18309 //===========================================================================================================================
18310
18311 static int
18312 _PrintFExtensionCallbackFlagsHandler(
18313 PrintFContext * inContext,
18314 PrintFFormat * inFormat,
18315 PrintFVAList * inArgs,
18316 void * inUserContext )
18317 {
18318 DNSServiceFlags flags;
18319 int n;
18320
18321 Unused( inUserContext );
18322
18323 flags = va_arg( inArgs->args, DNSServiceFlags );
18324 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18325
18326 n = PrintFCore( inContext, "%08X %s%c %c%c",
18327 flags, DNSServiceFlagsToAddRmvStr( flags ),
18328 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
18329 ( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
18330 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '*' : ' ' );
18331
18332 exit:
18333 return( n );
18334 }
18335
18336 //===========================================================================================================================
18337 // _PrintFExtensionDNSRecordDataHandler
18338 //===========================================================================================================================
18339
18340 static int
18341 _PrintFExtensionDNSRecordDataHandler(
18342 PrintFContext * inContext,
18343 PrintFFormat * inFormat,
18344 PrintFVAList * inArgs,
18345 void * inUserContext )
18346 {
18347 const void * rdataPtr;
18348 unsigned int rdataLen, rdataType;
18349 int n, fieldWidth;
18350
18351 Unused( inUserContext );
18352
18353 rdataType = va_arg( inArgs->args, unsigned int );
18354 rdataPtr = va_arg( inArgs->args, const void * );
18355 rdataLen = va_arg( inArgs->args, unsigned int );
18356 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18357
18358 check( inFormat->fieldWidth < INT_MAX );
18359 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
18360
18361 if( rdataLen > 0 )
18362 {
18363 char * rdataStr = NULL;
18364
18365 DNSRecordDataToString( rdataPtr, rdataLen, rdataType, NULL, 0, &rdataStr );
18366 if( rdataStr )
18367 {
18368 n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
18369 free( rdataStr );
18370 }
18371 else
18372 {
18373 n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
18374 }
18375 }
18376 else
18377 {
18378 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
18379 }
18380
18381 exit:
18382 return( n );
18383 }
18384
18385 //===========================================================================================================================
18386 // GetDNSSDFlagsFromOpts
18387 //===========================================================================================================================
18388
18389 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
18390 {
18391 DNSServiceFlags flags;
18392
18393 flags = (DNSServiceFlags) gDNSSDFlags;
18394 if( flags & kDNSServiceFlagsShareConnection )
18395 {
18396 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18397 kDNSServiceFlagsShareConnection );
18398 }
18399
18400 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
18401 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
18402 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
18403 if( gDNSSDFlag_DenyConstrained ) flags |= kDNSServiceFlagsDenyConstrained;
18404 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
18405 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
18406 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
18407 if( gDNSSDFlag_KnownUnique ) flags |= kDNSServiceFlagsKnownUnique;
18408 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
18409 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
18410 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
18411 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
18412 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
18413 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
18414 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
18415 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
18416 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
18417 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
18418
18419 return( flags );
18420 }
18421
18422 //===========================================================================================================================
18423 // CreateConnectionFromArgString
18424 //===========================================================================================================================
18425
18426 static OSStatus
18427 CreateConnectionFromArgString(
18428 const char * inString,
18429 dispatch_queue_t inQueue,
18430 DNSServiceRef * outSDRef,
18431 ConnectionDesc * outDesc )
18432 {
18433 OSStatus err;
18434 DNSServiceRef sdRef = NULL;
18435 ConnectionType type;
18436 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
18437 uint8_t uuid[ 16 ];
18438
18439 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
18440 {
18441 err = DNSServiceCreateConnection( &sdRef );
18442 require_noerr( err, exit );
18443 type = kConnectionType_Normal;
18444 }
18445 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
18446 {
18447 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
18448
18449 err = StringToInt32( pidStr, &pid );
18450 if( err )
18451 {
18452 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
18453 err = kParamErr;
18454 goto exit;
18455 }
18456
18457 memset( uuid, 0, sizeof( uuid ) );
18458 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
18459 if( err )
18460 {
18461 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
18462 goto exit;
18463 }
18464 type = kConnectionType_DelegatePID;
18465 }
18466 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
18467 {
18468 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
18469
18470 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
18471
18472 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
18473 if( err )
18474 {
18475 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
18476 err = kParamErr;
18477 goto exit;
18478 }
18479
18480 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
18481 if( err )
18482 {
18483 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
18484 goto exit;
18485 }
18486 type = kConnectionType_DelegateUUID;
18487 }
18488 else
18489 {
18490 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
18491 err = kParamErr;
18492 goto exit;
18493 }
18494
18495 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
18496 require_noerr( err, exit );
18497
18498 *outSDRef = sdRef;
18499 if( outDesc )
18500 {
18501 outDesc->type = type;
18502 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
18503 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
18504 }
18505 sdRef = NULL;
18506
18507 exit:
18508 if( sdRef ) DNSServiceRefDeallocate( sdRef );
18509 return( err );
18510 }
18511
18512 //===========================================================================================================================
18513 // InterfaceIndexFromArgString
18514 //===========================================================================================================================
18515
18516 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
18517 {
18518 OSStatus err;
18519 uint32_t ifIndex;
18520
18521 if( inString )
18522 {
18523 ifIndex = if_nametoindex( inString );
18524 if( ifIndex == 0 )
18525 {
18526 err = StringToUInt32( inString, &ifIndex );
18527 if( err )
18528 {
18529 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
18530 err = kParamErr;
18531 goto exit;
18532 }
18533 }
18534 }
18535 else
18536 {
18537 ifIndex = 0;
18538 }
18539
18540 *outIndex = ifIndex;
18541 err = kNoErr;
18542
18543 exit:
18544 return( err );
18545 }
18546
18547 //===========================================================================================================================
18548 // RecordDataFromArgString
18549 //===========================================================================================================================
18550
18551 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
18552 {
18553 OSStatus err;
18554 uint8_t * dataPtr = NULL;
18555 size_t dataLen;
18556
18557 if( 0 ) {}
18558
18559 // Domain name
18560
18561 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
18562 {
18563 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
18564
18565 err = StringToDomainName( str, &dataPtr, &dataLen );
18566 require_noerr_quiet( err, exit );
18567 }
18568
18569 // File path
18570
18571 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
18572 {
18573 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
18574
18575 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
18576 require_noerr( err, exit );
18577 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18578 }
18579
18580 // Hexadecimal string
18581
18582 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
18583 {
18584 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
18585
18586 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
18587 require_noerr( err, exit );
18588 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18589 }
18590
18591 // IPv4 address string
18592
18593 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
18594 {
18595 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
18596
18597 err = StringToARecordData( str, &dataPtr, &dataLen );
18598 require_noerr_quiet( err, exit );
18599 }
18600
18601 // IPv6 address string
18602
18603 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
18604 {
18605 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
18606
18607 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
18608 require_noerr_quiet( err, exit );
18609 }
18610
18611 // SRV record
18612
18613 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
18614 {
18615 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
18616
18617 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
18618 require_noerr( err, exit );
18619 }
18620
18621 // String with escaped hex and octal bytes
18622
18623 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
18624 {
18625 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
18626 const char * const end = str + strlen( str );
18627 size_t copiedLen;
18628 size_t totalLen;
18629 Boolean success;
18630
18631 if( str < end )
18632 {
18633 success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
18634 require_action( success, exit, err = kParamErr );
18635 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18636
18637 dataLen = totalLen;
18638 dataPtr = (uint8_t *) malloc( dataLen );
18639 require_action( dataPtr, exit, err = kNoMemoryErr );
18640
18641 success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
18642 require_action( success, exit, err = kParamErr );
18643 check( copiedLen == dataLen );
18644 }
18645 else
18646 {
18647 dataPtr = NULL;
18648 dataLen = 0;
18649 }
18650 }
18651
18652 // TXT record
18653
18654 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
18655 {
18656 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
18657
18658 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
18659 require_noerr( err, exit );
18660 }
18661
18662 // Unrecognized format
18663
18664 else
18665 {
18666 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
18667 err = kParamErr;
18668 goto exit;
18669 }
18670
18671 err = kNoErr;
18672 *outDataLen = dataLen;
18673 *outDataPtr = dataPtr;
18674 dataPtr = NULL;
18675
18676 exit:
18677 FreeNullSafe( dataPtr );
18678 return( err );
18679 }
18680
18681 //===========================================================================================================================
18682 // RecordTypeFromArgString
18683 //===========================================================================================================================
18684
18685 typedef struct
18686 {
18687 uint16_t value; // Record type's numeric value.
18688 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18689
18690 } RecordType;
18691
18692 static const RecordType kRecordTypes[] =
18693 {
18694 // Common types.
18695
18696 { kDNSServiceType_A, "A" },
18697 { kDNSServiceType_AAAA, "AAAA" },
18698 { kDNSServiceType_PTR, "PTR" },
18699 { kDNSServiceType_SRV, "SRV" },
18700 { kDNSServiceType_TXT, "TXT" },
18701 { kDNSServiceType_CNAME, "CNAME" },
18702 { kDNSServiceType_SOA, "SOA" },
18703 { kDNSServiceType_NSEC, "NSEC" },
18704 { kDNSServiceType_NS, "NS" },
18705 { kDNSServiceType_MX, "MX" },
18706 { kDNSServiceType_ANY, "ANY" },
18707 { kDNSServiceType_OPT, "OPT" },
18708
18709 // Less common types.
18710
18711 { kDNSServiceType_MD, "MD" },
18712 { kDNSServiceType_NS, "NS" },
18713 { kDNSServiceType_MD, "MD" },
18714 { kDNSServiceType_MF, "MF" },
18715 { kDNSServiceType_MB, "MB" },
18716 { kDNSServiceType_MG, "MG" },
18717 { kDNSServiceType_MR, "MR" },
18718 { kDNSServiceType_NULL, "NULL" },
18719 { kDNSServiceType_WKS, "WKS" },
18720 { kDNSServiceType_HINFO, "HINFO" },
18721 { kDNSServiceType_MINFO, "MINFO" },
18722 { kDNSServiceType_RP, "RP" },
18723 { kDNSServiceType_AFSDB, "AFSDB" },
18724 { kDNSServiceType_X25, "X25" },
18725 { kDNSServiceType_ISDN, "ISDN" },
18726 { kDNSServiceType_RT, "RT" },
18727 { kDNSServiceType_NSAP, "NSAP" },
18728 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
18729 { kDNSServiceType_SIG, "SIG" },
18730 { kDNSServiceType_KEY, "KEY" },
18731 { kDNSServiceType_PX, "PX" },
18732 { kDNSServiceType_GPOS, "GPOS" },
18733 { kDNSServiceType_LOC, "LOC" },
18734 { kDNSServiceType_NXT, "NXT" },
18735 { kDNSServiceType_EID, "EID" },
18736 { kDNSServiceType_NIMLOC, "NIMLOC" },
18737 { kDNSServiceType_ATMA, "ATMA" },
18738 { kDNSServiceType_NAPTR, "NAPTR" },
18739 { kDNSServiceType_KX, "KX" },
18740 { kDNSServiceType_CERT, "CERT" },
18741 { kDNSServiceType_A6, "A6" },
18742 { kDNSServiceType_DNAME, "DNAME" },
18743 { kDNSServiceType_SINK, "SINK" },
18744 { kDNSServiceType_APL, "APL" },
18745 { kDNSServiceType_DS, "DS" },
18746 { kDNSServiceType_SSHFP, "SSHFP" },
18747 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
18748 { kDNSServiceType_RRSIG, "RRSIG" },
18749 { kDNSServiceType_DNSKEY, "DNSKEY" },
18750 { kDNSServiceType_DHCID, "DHCID" },
18751 { kDNSServiceType_NSEC3, "NSEC3" },
18752 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
18753 { kDNSServiceType_HIP, "HIP" },
18754 { kDNSServiceType_SPF, "SPF" },
18755 { kDNSServiceType_UINFO, "UINFO" },
18756 { kDNSServiceType_UID, "UID" },
18757 { kDNSServiceType_GID, "GID" },
18758 { kDNSServiceType_UNSPEC, "UNSPEC" },
18759 { kDNSServiceType_TKEY, "TKEY" },
18760 { kDNSServiceType_TSIG, "TSIG" },
18761 { kDNSServiceType_IXFR, "IXFR" },
18762 { kDNSServiceType_AXFR, "AXFR" },
18763 { kDNSServiceType_MAILB, "MAILB" },
18764 { kDNSServiceType_MAILA, "MAILA" }
18765 };
18766
18767 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
18768 {
18769 OSStatus err;
18770 int32_t i32;
18771 const RecordType * type;
18772 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18773
18774 for( type = kRecordTypes; type < end; ++type )
18775 {
18776 if( strcasecmp( type->name, inString ) == 0 )
18777 {
18778 *outValue = type->value;
18779 return( kNoErr );
18780 }
18781 }
18782
18783 err = StringToInt32( inString, &i32 );
18784 require_noerr_quiet( err, exit );
18785 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18786
18787 *outValue = (uint16_t) i32;
18788
18789 exit:
18790 return( err );
18791 }
18792
18793 //===========================================================================================================================
18794 // RecordClassFromArgString
18795 //===========================================================================================================================
18796
18797 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
18798 {
18799 OSStatus err;
18800 int32_t i32;
18801
18802 if( strcasecmp( inString, "IN" ) == 0 )
18803 {
18804 *outValue = kDNSServiceClass_IN;
18805 err = kNoErr;
18806 goto exit;
18807 }
18808
18809 err = StringToInt32( inString, &i32 );
18810 require_noerr_quiet( err, exit );
18811 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18812
18813 *outValue = (uint16_t) i32;
18814
18815 exit:
18816 return( err );
18817 }
18818
18819 //===========================================================================================================================
18820 // InterfaceIndexToName
18821 //===========================================================================================================================
18822
18823 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
18824 {
18825 switch( inIfIndex )
18826 {
18827 case kDNSServiceInterfaceIndexAny:
18828 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
18829 break;
18830
18831 case kDNSServiceInterfaceIndexLocalOnly:
18832 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
18833 break;
18834
18835 case kDNSServiceInterfaceIndexUnicast:
18836 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
18837 break;
18838
18839 case kDNSServiceInterfaceIndexP2P:
18840 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
18841 break;
18842
18843 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18844 case kDNSServiceInterfaceIndexBLE:
18845 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
18846 break;
18847 #endif
18848
18849 default:
18850 {
18851 const char * name;
18852
18853 name = if_indextoname( inIfIndex, inNameBuf );
18854 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
18855 break;
18856 }
18857 }
18858
18859 return( inNameBuf );
18860 }
18861
18862 //===========================================================================================================================
18863 // RecordTypeToString
18864 //===========================================================================================================================
18865
18866 static const char * RecordTypeToString( unsigned int inValue )
18867 {
18868 const RecordType * type;
18869 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18870
18871 for( type = kRecordTypes; type < end; ++type )
18872 {
18873 if( type->value == inValue ) return( type->name );
18874 }
18875 return( "???" );
18876 }
18877
18878 //===========================================================================================================================
18879 // DNSRecordDataToString
18880 //===========================================================================================================================
18881
18882 static OSStatus
18883 DNSRecordDataToString(
18884 const void * inRDataPtr,
18885 size_t inRDataLen,
18886 unsigned int inRDataType,
18887 const void * inMsgPtr,
18888 size_t inMsgLen,
18889 char ** outString )
18890 {
18891 OSStatus err;
18892 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
18893 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
18894 char * rdataStr;
18895 const uint8_t * ptr;
18896 int n;
18897 char domainNameStr[ kDNSServiceMaxDomainName ];
18898
18899 rdataStr = NULL;
18900
18901 // A Record
18902
18903 if( inRDataType == kDNSServiceType_A )
18904 {
18905 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
18906
18907 ASPrintF( &rdataStr, "%.4a", rdataPtr );
18908 require_action( rdataStr, exit, err = kNoMemoryErr );
18909 }
18910
18911 // AAAA Record
18912
18913 else if( inRDataType == kDNSServiceType_AAAA )
18914 {
18915 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
18916
18917 ASPrintF( &rdataStr, "%.16a", rdataPtr );
18918 require_action( rdataStr, exit, err = kNoMemoryErr );
18919 }
18920
18921 // PTR, CNAME, or NS Record
18922
18923 else if( ( inRDataType == kDNSServiceType_PTR ) ||
18924 ( inRDataType == kDNSServiceType_CNAME ) ||
18925 ( inRDataType == kDNSServiceType_NS ) )
18926 {
18927 if( inMsgPtr )
18928 {
18929 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
18930 require_noerr( err, exit );
18931 }
18932 else
18933 {
18934 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
18935 require_noerr( err, exit );
18936 }
18937
18938 rdataStr = strdup( domainNameStr );
18939 require_action( rdataStr, exit, err = kNoMemoryErr );
18940 }
18941
18942 // SRV Record
18943
18944 else if( inRDataType == kDNSServiceType_SRV )
18945 {
18946 const dns_fixed_fields_srv * fields;
18947 const uint8_t * target;
18948 unsigned int priority, weight, port;
18949
18950 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
18951
18952 fields = (const dns_fixed_fields_srv *) rdataPtr;
18953 priority = dns_fixed_fields_srv_get_priority( fields );
18954 weight = dns_fixed_fields_srv_get_weight( fields );
18955 port = dns_fixed_fields_srv_get_port( fields );
18956 target = (const uint8_t *) &fields[ 1 ];
18957
18958 if( inMsgPtr )
18959 {
18960 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
18961 require_noerr( err, exit );
18962 }
18963 else
18964 {
18965 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
18966 require_noerr( err, exit );
18967 }
18968
18969 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
18970 require_action( rdataStr, exit, err = kNoMemoryErr );
18971 }
18972
18973 // TXT Record
18974
18975 else if( inRDataType == kDNSServiceType_TXT )
18976 {
18977 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
18978
18979 if( inRDataLen == 1 )
18980 {
18981 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
18982 require_action( rdataStr, exit, err = kNoMemoryErr );
18983 }
18984 else
18985 {
18986 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
18987 require_action( rdataStr, exit, err = kNoMemoryErr );
18988 }
18989 }
18990
18991 // SOA Record
18992
18993 else if( inRDataType == kDNSServiceType_SOA )
18994 {
18995 const dns_fixed_fields_soa * fields;
18996 uint32_t serial, refresh, retry, expire, minimum;
18997
18998 if( inMsgPtr )
18999 {
19000 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19001 require_noerr( err, exit );
19002
19003 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19004
19005 rdataStr = strdup( domainNameStr );
19006 require_action( rdataStr, exit, err = kNoMemoryErr );
19007
19008 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
19009 require_noerr( err, exit );
19010 }
19011 else
19012 {
19013 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19014 require_noerr( err, exit );
19015
19016 rdataStr = strdup( domainNameStr );
19017 require_action( rdataStr, exit, err = kNoMemoryErr );
19018
19019 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
19020 require_noerr( err, exit );
19021 }
19022
19023 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
19024
19025 fields = (const dns_fixed_fields_soa *) ptr;
19026 serial = dns_fixed_fields_soa_get_serial( fields );
19027 refresh = dns_fixed_fields_soa_get_refresh( fields );
19028 retry = dns_fixed_fields_soa_get_retry( fields );
19029 expire = dns_fixed_fields_soa_get_expire( fields );
19030 minimum = dns_fixed_fields_soa_get_minimum( fields );
19031
19032 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
19033 require_action( n > 0, exit, err = kUnknownErr );
19034 }
19035
19036 // NSEC Record
19037
19038 else if( inRDataType == kDNSServiceType_NSEC )
19039 {
19040 unsigned int windowBlock, bitmapLen, i, recordType;
19041 const uint8_t * bitmapPtr;
19042
19043 if( inMsgPtr )
19044 {
19045 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19046 require_noerr( err, exit );
19047 }
19048 else
19049 {
19050 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19051 require_noerr( err, exit );
19052 }
19053
19054 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19055
19056 rdataStr = strdup( domainNameStr );
19057 require_action( rdataStr, exit, err = kNoMemoryErr );
19058
19059 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
19060 {
19061 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19062
19063 windowBlock = ptr[ 0 ];
19064 bitmapLen = ptr[ 1 ];
19065 bitmapPtr = &ptr[ 2 ];
19066
19067 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
19068 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
19069
19070 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
19071 {
19072 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
19073 {
19074 recordType = ( windowBlock * 256 ) + i;
19075 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
19076 require_action( n > 0, exit, err = kUnknownErr );
19077 }
19078 }
19079 }
19080 }
19081
19082 // MX Record
19083
19084 else if( inRDataType == kDNSServiceType_MX )
19085 {
19086 uint16_t preference;
19087 const uint8_t * exchange;
19088
19089 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19090
19091 preference = ReadBig16( rdataPtr );
19092 exchange = &rdataPtr[ 2 ];
19093
19094 if( inMsgPtr )
19095 {
19096 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
19097 require_noerr( err, exit );
19098 }
19099 else
19100 {
19101 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
19102 require_noerr( err, exit );
19103 }
19104
19105 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
19106 require_action( n > 0, exit, err = kUnknownErr );
19107 }
19108
19109 // Unhandled record type
19110
19111 else
19112 {
19113 err = kNotHandledErr;
19114 goto exit;
19115 }
19116
19117 check( rdataStr );
19118 *outString = rdataStr;
19119 rdataStr = NULL;
19120 err = kNoErr;
19121
19122 exit:
19123 FreeNullSafe( rdataStr );
19124 return( err );
19125 }
19126
19127 //===========================================================================================================================
19128 // DNSMessageToText
19129 //===========================================================================================================================
19130
19131 #define DNSFlagsOpCodeToString( X ) ( \
19132 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19133 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19134 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19135 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19136 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19137 "Unassigned" )
19138
19139 #define DNSFlagsRCodeToString( X ) ( \
19140 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19141 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19142 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19143 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19144 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19145 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19146 "???" )
19147
19148 static OSStatus
19149 DNSMessageToText(
19150 const uint8_t * inMsgPtr,
19151 size_t inMsgLen,
19152 const Boolean inMDNS,
19153 const Boolean inPrintRaw,
19154 char ** outText )
19155 {
19156 OSStatus err;
19157 DataBuffer dataBuf;
19158 size_t len;
19159 const DNSHeader * hdr;
19160 const uint8_t * ptr;
19161 unsigned int id, flags, opcode, rcode;
19162 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
19163 uint8_t name[ kDomainNameLengthMax ];
19164 char nameStr[ kDNSServiceMaxDomainName ];
19165
19166 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
19167 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19168
19169 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
19170
19171 hdr = (DNSHeader *) inMsgPtr;
19172 id = DNSHeaderGetID( hdr );
19173 flags = DNSHeaderGetFlags( hdr );
19174 questionCount = DNSHeaderGetQuestionCount( hdr );
19175 answerCount = DNSHeaderGetAnswerCount( hdr );
19176 authorityCount = DNSHeaderGetAuthorityCount( hdr );
19177 additionalCount = DNSHeaderGetAdditionalCount( hdr );
19178 opcode = DNSFlagsGetOpCode( flags );
19179 rcode = DNSFlagsGetRCode( flags );
19180
19181 _Append( "ID: 0x%04X (%u)\n", id, id );
19182 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19183 flags,
19184 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
19185 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
19186 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
19187 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
19188 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
19189 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
19190 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
19191 DNSFlagsRCodeToString( rcode ) );
19192 _Append( "Question count: %u\n", questionCount );
19193 _Append( "Answer count: %u\n", answerCount );
19194 _Append( "Authority count: %u\n", authorityCount );
19195 _Append( "Additional count: %u\n", additionalCount );
19196
19197 ptr = (const uint8_t *) &hdr[ 1 ];
19198 for( i = 0; i < questionCount; ++i )
19199 {
19200 uint16_t qtype, qclass;
19201 Boolean isQU;
19202
19203 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
19204 require_noerr( err, exit );
19205
19206 err = DomainNameToString( name, NULL, nameStr, NULL );
19207 require_noerr( err, exit );
19208
19209 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
19210 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
19211
19212 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
19213
19214 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19215 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
19216 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
19217 }
19218
19219 totalRRCount = answerCount + authorityCount + additionalCount;
19220 for( i = 0; i < totalRRCount; ++i )
19221 {
19222 uint16_t type;
19223 uint16_t class;
19224 uint32_t ttl;
19225 const uint8_t * rdataPtr;
19226 size_t rdataLen;
19227 char * rdataStr;
19228 Boolean cacheFlush;
19229
19230 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
19231 require_noerr( err, exit );
19232
19233 err = DomainNameToString( name, NULL, nameStr, NULL );
19234 require_noerr( err, exit );
19235
19236 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
19237 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
19238
19239 rdataStr = NULL;
19240 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
19241 if( !rdataStr )
19242 {
19243 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, (int) rdataLen );
19244 require_action( rdataStr, exit, err = kNoMemoryErr );
19245 }
19246
19247 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
19248 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
19249 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
19250
19251 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19252 nameStr, ttl, cacheFlush ? "CF" : "",
19253 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
19254 RecordTypeToString( type ), rdataStr );
19255 free( rdataStr );
19256 }
19257 _Append( "\n" );
19258
19259 err = DataBuffer_Append( &dataBuf, "", 1 );
19260 require_noerr( err, exit );
19261
19262 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
19263 require_noerr( err, exit );
19264
19265 exit:
19266 DataBuffer_Free( &dataBuf );
19267 return( err );
19268 }
19269
19270 //===========================================================================================================================
19271 // WriteDNSQueryMessage
19272 //===========================================================================================================================
19273
19274 static OSStatus
19275 WriteDNSQueryMessage(
19276 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
19277 uint16_t inMsgID,
19278 uint16_t inFlags,
19279 const char * inQName,
19280 uint16_t inQType,
19281 uint16_t inQClass,
19282 size_t * outMsgLen )
19283 {
19284 OSStatus err;
19285 uint8_t qname[ kDomainNameLengthMax ];
19286
19287 err = DomainNameFromString( qname, inQName, NULL );
19288 require_noerr_quiet( err, exit );
19289
19290 err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
19291 require_noerr_quiet( err, exit );
19292
19293 exit:
19294 return( err );
19295 }
19296
19297 //===========================================================================================================================
19298 // DispatchSignalSourceCreate
19299 //===========================================================================================================================
19300
19301 static OSStatus
19302 DispatchSignalSourceCreate(
19303 int inSignal,
19304 DispatchHandler inEventHandler,
19305 void * inContext,
19306 dispatch_source_t * outSource )
19307 {
19308 OSStatus err;
19309 dispatch_source_t source;
19310
19311 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
19312 require_action( source, exit, err = kUnknownErr );
19313
19314 dispatch_set_context( source, inContext );
19315 dispatch_source_set_event_handler_f( source, inEventHandler );
19316
19317 *outSource = source;
19318 err = kNoErr;
19319
19320 exit:
19321 return( err );
19322 }
19323
19324 //===========================================================================================================================
19325 // DispatchSocketSourceCreate
19326 //===========================================================================================================================
19327
19328 static OSStatus
19329 DispatchSocketSourceCreate(
19330 SocketRef inSock,
19331 dispatch_source_type_t inType,
19332 dispatch_queue_t inQueue,
19333 DispatchHandler inEventHandler,
19334 DispatchHandler inCancelHandler,
19335 void * inContext,
19336 dispatch_source_t * outSource )
19337 {
19338 OSStatus err;
19339 dispatch_source_t source;
19340
19341 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19342 require_action( source, exit, err = kUnknownErr );
19343
19344 dispatch_set_context( source, inContext );
19345 dispatch_source_set_event_handler_f( source, inEventHandler );
19346 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
19347
19348 *outSource = source;
19349 err = kNoErr;
19350
19351 exit:
19352 return( err );
19353 }
19354
19355 //===========================================================================================================================
19356 // DispatchTimerCreate
19357 //===========================================================================================================================
19358
19359 static OSStatus
19360 DispatchTimerCreate(
19361 dispatch_time_t inStart,
19362 uint64_t inIntervalNs,
19363 uint64_t inLeewayNs,
19364 dispatch_queue_t inQueue,
19365 DispatchHandler inEventHandler,
19366 DispatchHandler inCancelHandler,
19367 void * inContext,
19368 dispatch_source_t * outTimer )
19369 {
19370 OSStatus err;
19371 dispatch_source_t timer;
19372
19373 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19374 require_action( timer, exit, err = kUnknownErr );
19375
19376 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
19377 dispatch_set_context( timer, inContext );
19378 dispatch_source_set_event_handler_f( timer, inEventHandler );
19379 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
19380
19381 *outTimer = timer;
19382 err = kNoErr;
19383
19384 exit:
19385 return( err );
19386 }
19387
19388 //===========================================================================================================================
19389 // DispatchProcessMonitorCreate
19390 //===========================================================================================================================
19391
19392 static OSStatus
19393 DispatchProcessMonitorCreate(
19394 pid_t inPID,
19395 unsigned long inFlags,
19396 dispatch_queue_t inQueue,
19397 DispatchHandler inEventHandler,
19398 DispatchHandler inCancelHandler,
19399 void * inContext,
19400 dispatch_source_t * outMonitor )
19401 {
19402 OSStatus err;
19403 dispatch_source_t monitor;
19404
19405 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
19406 inQueue ? inQueue : dispatch_get_main_queue() );
19407 require_action( monitor, exit, err = kUnknownErr );
19408
19409 dispatch_set_context( monitor, inContext );
19410 dispatch_source_set_event_handler_f( monitor, inEventHandler );
19411 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
19412
19413 *outMonitor = monitor;
19414 err = kNoErr;
19415
19416 exit:
19417 return( err );
19418 }
19419
19420 //===========================================================================================================================
19421 // ServiceTypeDescription
19422 //===========================================================================================================================
19423
19424 typedef struct
19425 {
19426 const char * name; // Name of the service type in two-label "_service._proto" format.
19427 const char * description; // Description of the service type.
19428
19429 } ServiceType;
19430
19431 // A Non-comprehensive table of DNS-SD service types
19432
19433 static const ServiceType kServiceTypes[] =
19434 {
19435 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19436 { "_adisk._tcp", "Automatic Disk Discovery" },
19437 { "_afpovertcp._tcp", "Apple File Sharing" },
19438 { "_airdrop._tcp", "AirDrop" },
19439 { "_airplay._tcp", "AirPlay" },
19440 { "_airport._tcp", "AirPort Base Station" },
19441 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19442 { "_eppc._tcp", "Remote AppleEvents" },
19443 { "_ftp._tcp", "File Transfer Protocol" },
19444 { "_home-sharing._tcp", "Home Sharing" },
19445 { "_homekit._tcp", "HomeKit" },
19446 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19447 { "_https._tcp", "HTTP over SSL/TLS" },
19448 { "_ipp._tcp", "Internet Printing Protocol" },
19449 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19450 { "_mediaremotetv._tcp", "Media Remote" },
19451 { "_net-assistant._tcp", "Apple Remote Desktop" },
19452 { "_od-master._tcp", "OpenDirectory Master" },
19453 { "_nfs._tcp", "Network File System" },
19454 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19455 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19456 { "_raop._tcp", "Remote Audio Output Protocol" },
19457 { "_rfb._tcp", "Remote Frame Buffer" },
19458 { "_scanner._tcp", "Bonjour Scanning" },
19459 { "_smb._tcp", "Server Message Block over TCP/IP" },
19460 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19461 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19462 { "_ssh._tcp", "SSH Remote Login Protocol" },
19463 { "_teleport._tcp", "teleport" },
19464 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19465 { "_workstation._tcp", "Workgroup Manager" },
19466 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19467 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19468 };
19469
19470 static const char * ServiceTypeDescription( const char *inName )
19471 {
19472 const ServiceType * serviceType;
19473 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
19474
19475 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
19476 {
19477 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
19478 {
19479 const char * const ptr = &inName[ strlen( serviceType->name ) ];
19480
19481 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
19482 {
19483 return( serviceType->description );
19484 }
19485 }
19486 }
19487 return( NULL );
19488 }
19489
19490 //===========================================================================================================================
19491 // SocketContextCreate
19492 //===========================================================================================================================
19493
19494 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
19495 {
19496 OSStatus err;
19497 SocketContext * context;
19498
19499 context = (SocketContext *) calloc( 1, sizeof( *context ) );
19500 require_action( context, exit, err = kNoMemoryErr );
19501
19502 context->refCount = 1;
19503 context->sock = inSock;
19504 context->userContext = inUserContext;
19505
19506 *outContext = context;
19507 err = kNoErr;
19508
19509 exit:
19510 return( err );
19511 }
19512
19513 //===========================================================================================================================
19514 // SocketContextRetain
19515 //===========================================================================================================================
19516
19517 static SocketContext * SocketContextRetain( SocketContext *inContext )
19518 {
19519 ++inContext->refCount;
19520 return( inContext );
19521 }
19522
19523 //===========================================================================================================================
19524 // SocketContextRelease
19525 //===========================================================================================================================
19526
19527 static void SocketContextRelease( SocketContext *inContext )
19528 {
19529 if( --inContext->refCount == 0 )
19530 {
19531 ForgetSocket( &inContext->sock );
19532 free( inContext );
19533 }
19534 }
19535
19536 //===========================================================================================================================
19537 // SocketContextCancelHandler
19538 //===========================================================================================================================
19539
19540 static void SocketContextCancelHandler( void *inContext )
19541 {
19542 SocketContextRelease( (SocketContext *) inContext );
19543 }
19544
19545 //===========================================================================================================================
19546 // StringToInt32
19547 //===========================================================================================================================
19548
19549 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
19550 {
19551 OSStatus err;
19552 long value;
19553 char * endPtr;
19554
19555 value = strtol( inString, &endPtr, 0 );
19556 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19557 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
19558
19559 *outValue = (int32_t) value;
19560 err = kNoErr;
19561
19562 exit:
19563 return( err );
19564 }
19565
19566 //===========================================================================================================================
19567 // StringToUInt32
19568 //===========================================================================================================================
19569
19570 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
19571 {
19572 OSStatus err;
19573 uint32_t value;
19574 char * endPtr;
19575
19576 value = (uint32_t) strtol( inString, &endPtr, 0 );
19577 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19578
19579 *outValue = value;
19580 err = kNoErr;
19581
19582 exit:
19583 return( err );
19584 }
19585
19586 #if( TARGET_OS_DARWIN )
19587 //===========================================================================================================================
19588 // _StringToInt64
19589 //===========================================================================================================================
19590
19591 static int64_t _StringToInt64( const char *inString, OSStatus *outError )
19592 {
19593 OSStatus err;
19594 long long val;
19595 char * end;
19596 int errnoVal;
19597
19598 set_errno_compat( 0 );
19599 val = strtoll( inString, &end, 0 );
19600 errnoVal = errno_compat();
19601
19602 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19603 require_action_quiet( ( ( val != LLONG_MIN ) && ( val != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19604 require_action_quiet( ( val >= INT64_MIN ) && ( val <= INT64_MAX ), exit, err = kRangeErr );
19605 err = kNoErr;
19606
19607 exit:
19608 if( outError ) *outError = err;
19609 return( (int64_t)val );
19610 }
19611
19612 //===========================================================================================================================
19613 // _StringToUInt64
19614 //===========================================================================================================================
19615
19616 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError )
19617 {
19618 OSStatus err;
19619 unsigned long long val;
19620 char * end;
19621 int errnoVal;
19622
19623 set_errno_compat( 0 );
19624 val = strtoull( inString, &end, 0 );
19625 errnoVal = errno_compat();
19626
19627 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19628 require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19629 require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
19630 err = kNoErr;
19631
19632 exit:
19633 if( outError ) *outError = err;
19634 return( (uint64_t)val );
19635 }
19636
19637 //===========================================================================================================================
19638 // _StringToPID
19639 //===========================================================================================================================
19640
19641 static pid_t _StringToPID( const char *inString, OSStatus *outError )
19642 {
19643 OSStatus err;
19644 int64_t val;
19645
19646 val = _StringToInt64( inString, &err );
19647 require_noerr_quiet( err, exit );
19648 require_action_quiet( val == (pid_t) val, exit, err = kRangeErr );
19649 err = kNoErr;
19650
19651 exit:
19652 if( outError ) *outError = err;
19653 return( (pid_t) val );
19654 }
19655
19656 //===========================================================================================================================
19657 // _ParseEscapedString
19658 //
19659 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19660 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19661 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19662 //===========================================================================================================================
19663
19664 static OSStatus
19665 _ParseEscapedString(
19666 const char * inSrc,
19667 const char * inEnd,
19668 const char * inDelimiters,
19669 char * inBufPtr,
19670 size_t inBufLen,
19671 size_t * outCopiedLen,
19672 size_t * outActualLen,
19673 const char ** outPtr )
19674 {
19675 OSStatus err;
19676 const char * ptr;
19677 char * dst = inBufPtr;
19678 const char * const lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
19679 size_t len;
19680
19681 len = 0;
19682 ptr = inSrc;
19683 if( !inDelimiters ) inDelimiters = "";
19684 while( ptr < inEnd )
19685 {
19686 int c;
19687 const char * del;
19688
19689 c = *ptr;
19690 for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
19691 if( *del != '\0' ) break;
19692 ++ptr;
19693 if( c == '\\' )
19694 {
19695 require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
19696 c = *ptr++;
19697 }
19698 ++len;
19699 if( dst < lim ) *dst++ = (char) c;
19700 }
19701 if( inBufLen > 0 ) *dst = '\0';
19702
19703 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBufPtr );
19704 if( outActualLen ) *outActualLen = len;
19705 if( outPtr ) *outPtr = ptr;
19706 err = kNoErr;
19707
19708 exit:
19709 return( err );
19710 }
19711 #endif
19712
19713 //===========================================================================================================================
19714 // StringToARecordData
19715 //===========================================================================================================================
19716
19717 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19718 {
19719 OSStatus err;
19720 uint32_t * addrPtr;
19721 const size_t addrLen = sizeof( *addrPtr );
19722 const char * end;
19723
19724 addrPtr = (uint32_t *) malloc( addrLen );
19725 require_action( addrPtr, exit, err = kNoMemoryErr );
19726
19727 err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
19728 NULL, NULL, NULL, &end );
19729 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19730 require_noerr_quiet( err, exit );
19731
19732 *addrPtr = HostToBig32( *addrPtr );
19733
19734 *outPtr = (uint8_t *) addrPtr;
19735 addrPtr = NULL;
19736 *outLen = addrLen;
19737
19738 exit:
19739 FreeNullSafe( addrPtr );
19740 return( err );
19741 }
19742
19743 //===========================================================================================================================
19744 // StringToAAAARecordData
19745 //===========================================================================================================================
19746
19747 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19748 {
19749 OSStatus err;
19750 uint8_t * addrPtr;
19751 const size_t addrLen = 16;
19752 const char * end;
19753
19754 addrPtr = (uint8_t *) malloc( addrLen );
19755 require_action( addrPtr, exit, err = kNoMemoryErr );
19756
19757 err = _StringToIPv6Address( inString,
19758 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
19759 addrPtr, NULL, NULL, NULL, &end );
19760 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19761 require_noerr_quiet( err, exit );
19762
19763 *outPtr = addrPtr;
19764 addrPtr = NULL;
19765 *outLen = addrLen;
19766
19767 exit:
19768 FreeNullSafe( addrPtr );
19769 return( err );
19770 }
19771
19772 //===========================================================================================================================
19773 // StringToDomainName
19774 //===========================================================================================================================
19775
19776 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
19777 {
19778 OSStatus err;
19779 uint8_t * namePtr;
19780 size_t nameLen;
19781 uint8_t * end;
19782 uint8_t nameBuf[ kDomainNameLengthMax ];
19783
19784 err = DomainNameFromString( nameBuf, inString, &end );
19785 require_noerr_quiet( err, exit );
19786
19787 nameLen = (size_t)( end - nameBuf );
19788 namePtr = _memdup( nameBuf, nameLen );
19789 require_action( namePtr, exit, err = kNoMemoryErr );
19790
19791 *outPtr = namePtr;
19792 namePtr = NULL;
19793 if( outLen ) *outLen = nameLen;
19794
19795 exit:
19796 return( err );
19797 }
19798
19799 #if( TARGET_OS_DARWIN )
19800 //===========================================================================================================================
19801 // GetDefaultDNSServer
19802 //===========================================================================================================================
19803
19804 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
19805 {
19806 OSStatus err;
19807 dns_config_t * config;
19808 struct sockaddr * addr;
19809 int32_t i;
19810
19811 config = dns_configuration_copy();
19812 require_action( config, exit, err = kUnknownErr );
19813
19814 addr = NULL;
19815 for( i = 0; i < config->n_resolver; ++i )
19816 {
19817 const dns_resolver_t * const resolver = config->resolver[ i ];
19818
19819 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
19820 {
19821 addr = resolver->nameserver[ 0 ];
19822 break;
19823 }
19824 }
19825 require_action_quiet( addr, exit, err = kNotFoundErr );
19826
19827 SockAddrCopy( addr, outAddr );
19828 err = kNoErr;
19829
19830 exit:
19831 if( config ) dns_configuration_free( config );
19832 return( err );
19833 }
19834 #endif
19835
19836 //===========================================================================================================================
19837 // GetMDNSMulticastAddrV4
19838 //===========================================================================================================================
19839
19840 static void _MDNSMulticastAddrV4Init( void *inContext );
19841
19842 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
19843 {
19844 static struct sockaddr_in sMDNSMulticastAddrV4;
19845 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
19846
19847 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
19848 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
19849 }
19850
19851 static void _MDNSMulticastAddrV4Init( void *inContext )
19852 {
19853 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
19854
19855 memset( addr, 0, sizeof( *addr ) );
19856 SIN_LEN_SET( addr );
19857 addr->sin_family = AF_INET;
19858 addr->sin_port = htons( kMDNSPort );
19859 addr->sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19860 }
19861
19862 //===========================================================================================================================
19863 // GetMDNSMulticastAddrV6
19864 //===========================================================================================================================
19865
19866 static void _MDNSMulticastAddrV6Init( void *inContext );
19867
19868 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
19869 {
19870 static struct sockaddr_in6 sMDNSMulticastAddrV6;
19871 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
19872
19873 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
19874 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
19875 }
19876
19877 static void _MDNSMulticastAddrV6Init( void *inContext )
19878 {
19879 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
19880
19881 memset( addr, 0, sizeof( *addr ) );
19882 SIN6_LEN_SET( addr );
19883 addr->sin6_family = AF_INET6;
19884 addr->sin6_port = htons( kMDNSPort );
19885 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19886 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
19887 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
19888 }
19889
19890 //===========================================================================================================================
19891 // CreateMulticastSocket
19892 //===========================================================================================================================
19893
19894 static OSStatus
19895 CreateMulticastSocket(
19896 const struct sockaddr * inAddr,
19897 int inPort,
19898 const char * inIfName,
19899 uint32_t inIfIndex,
19900 Boolean inJoin,
19901 int * outPort,
19902 SocketRef * outSock )
19903 {
19904 OSStatus err;
19905 SocketRef sock = kInvalidSocketRef;
19906 const int family = inAddr->sa_family;
19907 int port;
19908
19909 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
19910
19911 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
19912 require_noerr_quiet( err, exit );
19913
19914 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
19915 require_noerr_quiet( err, exit );
19916
19917 if( family == AF_INET )
19918 {
19919 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
19920 err = map_socket_noerr_errno( sock, err );
19921 require_noerr_quiet( err, exit );
19922 }
19923 else
19924 {
19925 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
19926 err = map_socket_noerr_errno( sock, err );
19927 require_noerr_quiet( err, exit );
19928 }
19929
19930 if( inJoin )
19931 {
19932 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
19933 require_noerr_quiet( err, exit );
19934 }
19935
19936 if( outPort ) *outPort = port;
19937 *outSock = sock;
19938 sock = kInvalidSocketRef;
19939
19940 exit:
19941 ForgetSocket( &sock );
19942 return( err );
19943 }
19944
19945 //===========================================================================================================================
19946 // DecimalTextToUInt32
19947 //===========================================================================================================================
19948
19949 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
19950 {
19951 OSStatus err;
19952 uint64_t value;
19953 const char * ptr = inSrc;
19954
19955 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
19956
19957 value = (uint64_t)( *ptr++ - '0' );
19958 if( value == 0 )
19959 {
19960 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
19961 {
19962 err = kMalformedErr;
19963 goto exit;
19964 }
19965 }
19966 else
19967 {
19968 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
19969 {
19970 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
19971 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
19972 }
19973 }
19974
19975 *outValue = (uint32_t) value;
19976 if( outPtr ) *outPtr = ptr;
19977 err = kNoErr;
19978
19979 exit:
19980 return( err );
19981 }
19982
19983 //===========================================================================================================================
19984 // CheckIntegerArgument
19985 //===========================================================================================================================
19986
19987 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
19988 {
19989 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
19990
19991 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
19992 return( kRangeErr );
19993 }
19994
19995 //===========================================================================================================================
19996 // CheckDoubleArgument
19997 //===========================================================================================================================
19998
19999 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
20000 {
20001 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
20002
20003 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
20004 return( kRangeErr );
20005 }
20006
20007 //===========================================================================================================================
20008 // CheckRootUser
20009 //===========================================================================================================================
20010
20011 static OSStatus CheckRootUser( void )
20012 {
20013 if( geteuid() == 0 ) return( kNoErr );
20014
20015 FPrintF( stderr, "error: This command must to be run as root.\n" );
20016 return( kPermissionErr );
20017 }
20018
20019 //===========================================================================================================================
20020 // SpawnCommand
20021 //
20022 // Note: Based on systemf() from CoreUtils framework.
20023 //===========================================================================================================================
20024
20025 extern char ** environ;
20026
20027 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... )
20028 {
20029 OSStatus err;
20030 va_list args;
20031 char * command;
20032 char * argv[ 4 ];
20033 pid_t pid;
20034
20035 command = NULL;
20036 va_start( args, inFormat );
20037 VASPrintF( &command, inFormat, args );
20038 va_end( args );
20039 require_action( command, exit, err = kUnknownErr );
20040
20041 argv[ 0 ] = "/bin/sh";
20042 argv[ 1 ] = "-c";
20043 argv[ 2 ] = command;
20044 argv[ 3 ] = NULL;
20045 err = posix_spawn( &pid, argv[ 0 ], NULL, NULL, argv, environ );
20046 free( command );
20047 require_noerr_quiet( err, exit );
20048
20049 if( outPID ) *outPID = pid;
20050
20051 exit:
20052 return( err );
20053 }
20054
20055 //===========================================================================================================================
20056 // OutputFormatFromArgString
20057 //===========================================================================================================================
20058
20059 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
20060 {
20061 OSStatus err;
20062 OutputFormatType format;
20063
20064 format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
20065 kOutputFormatStr_JSON, kOutputFormatType_JSON,
20066 kOutputFormatStr_XML, kOutputFormatType_XML,
20067 kOutputFormatStr_Binary, kOutputFormatType_Binary,
20068 NULL );
20069 if( outFormat ) *outFormat = format;
20070 return( err );
20071 }
20072
20073 //===========================================================================================================================
20074 // OutputPropertyList
20075 //===========================================================================================================================
20076
20077 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
20078 {
20079 OSStatus err;
20080 CFDataRef results = NULL;
20081 FILE * file = NULL;
20082
20083 // Convert plist to a specific format.
20084
20085 switch( inType )
20086 {
20087 case kOutputFormatType_JSON:
20088 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
20089 require_action( results, exit, err = kUnknownErr );
20090 break;
20091
20092 case kOutputFormatType_XML:
20093 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
20094 require_action( results, exit, err = kUnknownErr );
20095 break;
20096
20097 case kOutputFormatType_Binary:
20098 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
20099 require_action( results, exit, err = kUnknownErr );
20100 break;
20101
20102 default:
20103 err = kTypeErr;
20104 goto exit;
20105 }
20106
20107 // Write formatted results to file or stdout.
20108
20109 if( inOutputFilePath )
20110 {
20111 file = fopen( inOutputFilePath, "wb" );
20112 err = map_global_value_errno( file, file );
20113 require_noerr( err, exit );
20114 }
20115 else
20116 {
20117 file = stdout;
20118 }
20119
20120 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
20121 require_noerr_quiet( err, exit );
20122
20123 // Write a trailing newline for JSON-formatted results.
20124
20125 if( inType == kOutputFormatType_JSON )
20126 {
20127 err = WriteANSIFile( file, "\n", 1 );
20128 require_noerr_quiet( err, exit );
20129 }
20130
20131 exit:
20132 if( file && ( file != stdout ) ) fclose( file );
20133 CFReleaseNullSafe( results );
20134 return( err );
20135 }
20136
20137 //===========================================================================================================================
20138 // CreateSRVRecordDataFromString
20139 //===========================================================================================================================
20140
20141 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
20142 {
20143 OSStatus err;
20144 DataBuffer dataBuf;
20145 const char * ptr;
20146 int i;
20147 uint8_t * end;
20148 uint8_t target[ kDomainNameLengthMax ];
20149
20150 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
20151
20152 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20153
20154 ptr = inString;
20155 for( i = 0; i < 3; ++i )
20156 {
20157 char * next;
20158 long value;
20159 uint8_t buf[ 2 ];
20160
20161 value = strtol( ptr, &next, 0 );
20162 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
20163 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
20164 ptr = next + 1;
20165
20166 WriteBig16( buf, value );
20167
20168 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
20169 require_noerr( err, exit );
20170 }
20171
20172 // Set the target domain name.
20173
20174 err = DomainNameFromString( target, ptr, &end );
20175 require_noerr_quiet( err, exit );
20176
20177 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
20178 require_noerr( err, exit );
20179
20180 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20181 require_noerr( err, exit );
20182
20183 exit:
20184 DataBuffer_Free( &dataBuf );
20185 return( err );
20186 }
20187
20188 //===========================================================================================================================
20189 // CreateTXTRecordDataFromString
20190 //===========================================================================================================================
20191
20192 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
20193 {
20194 OSStatus err;
20195 DataBuffer dataBuf;
20196 const char * src;
20197 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20198
20199 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
20200
20201 src = inString;
20202 for( ;; )
20203 {
20204 uint8_t * dst = &txtStr[ 1 ];
20205 const uint8_t * const lim = &txtStr[ 256 ];
20206 int c;
20207
20208 while( *src && ( *src != inDelimiter ) )
20209 {
20210 if( ( c = *src++ ) == '\\' )
20211 {
20212 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
20213 c = *src++;
20214 }
20215 require_action_quiet( dst < lim, exit, err = kOverrunErr );
20216 *dst++ = (uint8_t) c;
20217 }
20218 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
20219 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
20220 require_noerr( err, exit );
20221
20222 if( *src == '\0' ) break;
20223 ++src;
20224 }
20225
20226 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20227 require_noerr( err, exit );
20228
20229 exit:
20230 DataBuffer_Free( &dataBuf );
20231 return( err );
20232 }
20233
20234 //===========================================================================================================================
20235 // CreateNSECRecordData
20236 //===========================================================================================================================
20237
20238 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
20239 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
20240
20241 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20242
20243 static OSStatus
20244 CreateNSECRecordData(
20245 const uint8_t * inNextDomainName,
20246 uint8_t ** outPtr,
20247 size_t * outLen,
20248 unsigned int inTypeCount,
20249 ... )
20250 {
20251 OSStatus err;
20252 va_list args;
20253 DataBuffer rdataDB;
20254 unsigned int * array = NULL;
20255 unsigned int i, type, maxBit, currBlock, bitmapLen;
20256 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
20257 uint8_t * const bitmap = &fields[ 2 ];
20258
20259 va_start( args, inTypeCount );
20260 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20261
20262 // Append Next Domain Name.
20263
20264 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
20265 require_noerr( err, exit );
20266
20267 // Append Type Bit Maps.
20268
20269 maxBit = 0;
20270 memset( bitmap, 0, kNSECBitmapMaxLength );
20271 if( inTypeCount > 0 )
20272 {
20273 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
20274 require_action( array, exit, err = kNoMemoryErr );
20275
20276 for( i = 0; i < inTypeCount; ++i )
20277 {
20278 type = va_arg( args, unsigned int );
20279 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
20280 array[ i ] = type;
20281 }
20282 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
20283
20284 currBlock = array[ 0 ] / 256;
20285 for( i = 0; i < inTypeCount; ++i )
20286 {
20287 const unsigned int block = array[ i ] / 256;
20288 const unsigned int bit = array[ i ] % 256;
20289
20290 if( block != currBlock )
20291 {
20292 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20293 fields[ 0 ] = (uint8_t) currBlock;
20294 fields[ 1 ] = (uint8_t) bitmapLen;
20295
20296 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20297 require_noerr( err, exit );
20298
20299 maxBit = 0;
20300 currBlock = block;
20301 memset( bitmap, 0, bitmapLen );
20302 }
20303 BitArray_SetBit( bitmap, bit );
20304 if( bit > maxBit ) maxBit = bit;
20305 }
20306 }
20307 else
20308 {
20309 currBlock = 0;
20310 }
20311
20312 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20313 fields[ 0 ] = (uint8_t) currBlock;
20314 fields[ 1 ] = (uint8_t) bitmapLen;
20315
20316 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20317 require_noerr( err, exit );
20318
20319 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20320 require_noerr( err, exit );
20321
20322 exit:
20323 va_end( args );
20324 DataBuffer_Free( &rdataDB );
20325 FreeNullSafe( array );
20326 return( err );
20327 }
20328
20329 //===========================================================================================================================
20330 // AppendSOARecord
20331 //===========================================================================================================================
20332
20333 static OSStatus
20334 _AppendSOARecordData(
20335 DataBuffer * inDB,
20336 const uint8_t * inMName,
20337 const uint8_t * inRName,
20338 uint32_t inSerial,
20339 uint32_t inRefresh,
20340 uint32_t inRetry,
20341 uint32_t inExpire,
20342 uint32_t inMinimumTTL,
20343 size_t * outLen );
20344
20345 static OSStatus
20346 AppendSOARecord(
20347 DataBuffer * inDB,
20348 const uint8_t * inNamePtr,
20349 size_t inNameLen,
20350 uint16_t inType,
20351 uint16_t inClass,
20352 uint32_t inTTL,
20353 const uint8_t * inMName,
20354 const uint8_t * inRName,
20355 uint32_t inSerial,
20356 uint32_t inRefresh,
20357 uint32_t inRetry,
20358 uint32_t inExpire,
20359 uint32_t inMinimumTTL,
20360 size_t * outLen )
20361 {
20362 OSStatus err;
20363 size_t rdataLen;
20364 size_t rdlengthOffset = 0;
20365 uint8_t * rdlengthPtr;
20366
20367 if( inDB )
20368 {
20369 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
20370 require_noerr( err, exit );
20371
20372 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
20373 }
20374
20375 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
20376 require_noerr( err, exit );
20377
20378 if( inDB )
20379 {
20380 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
20381 WriteBig16( rdlengthPtr, rdataLen );
20382 }
20383
20384 if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
20385 err = kNoErr;
20386
20387 exit:
20388 return( err );
20389 }
20390
20391 static OSStatus
20392 _AppendSOARecordData(
20393 DataBuffer * inDB,
20394 const uint8_t * inMName,
20395 const uint8_t * inRName,
20396 uint32_t inSerial,
20397 uint32_t inRefresh,
20398 uint32_t inRetry,
20399 uint32_t inExpire,
20400 uint32_t inMinimumTTL,
20401 size_t * outLen )
20402 {
20403 OSStatus err;
20404 dns_fixed_fields_soa fields;
20405 const size_t mnameLen = DomainNameLength( inMName );
20406 const size_t rnameLen = DomainNameLength( inRName );
20407
20408 if( inDB )
20409 {
20410 err = DataBuffer_Append( inDB, inMName, mnameLen );
20411 require_noerr( err, exit );
20412
20413 err = DataBuffer_Append( inDB, inRName, rnameLen );
20414 require_noerr( err, exit );
20415
20416 dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
20417 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20418 require_noerr( err, exit );
20419 }
20420 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
20421 err = kNoErr;
20422
20423 exit:
20424 return( err );
20425 }
20426
20427 //===========================================================================================================================
20428 // CreateSOARecordData
20429 //===========================================================================================================================
20430
20431 static OSStatus
20432 CreateSOARecordData(
20433 const uint8_t * inMName,
20434 const uint8_t * inRName,
20435 uint32_t inSerial,
20436 uint32_t inRefresh,
20437 uint32_t inRetry,
20438 uint32_t inExpire,
20439 uint32_t inMinimumTTL,
20440 uint8_t ** outPtr,
20441 size_t * outLen )
20442 {
20443 OSStatus err;
20444 DataBuffer rdataDB;
20445
20446 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20447
20448 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
20449 require_noerr( err, exit );
20450
20451 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20452 require_noerr( err, exit );
20453
20454 exit:
20455 DataBuffer_Free( &rdataDB );
20456 return( err );
20457 }
20458
20459 //===========================================================================================================================
20460 // _DataBuffer_AppendDNSQuestion
20461 //===========================================================================================================================
20462
20463 static OSStatus
20464 _DataBuffer_AppendDNSQuestion(
20465 DataBuffer * inDB,
20466 const uint8_t * inNamePtr,
20467 size_t inNameLen,
20468 uint16_t inType,
20469 uint16_t inClass )
20470 {
20471 OSStatus err;
20472 dns_fixed_fields_question fields;
20473
20474 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20475 require_noerr( err, exit );
20476
20477 dns_fixed_fields_question_init( &fields, inType, inClass );
20478 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20479 require_noerr( err, exit );
20480
20481 exit:
20482 return( err );
20483 }
20484
20485 //===========================================================================================================================
20486 // _DataBuffer_AppendDNSRecord
20487 //===========================================================================================================================
20488
20489 static OSStatus
20490 _DataBuffer_AppendDNSRecord(
20491 DataBuffer * inDB,
20492 const uint8_t * inNamePtr,
20493 size_t inNameLen,
20494 uint16_t inType,
20495 uint16_t inClass,
20496 uint32_t inTTL,
20497 const uint8_t * inRDataPtr,
20498 size_t inRDataLen )
20499 {
20500 OSStatus err;
20501 dns_fixed_fields_record fields;
20502
20503 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
20504
20505 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20506 require_noerr( err, exit );
20507
20508 dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
20509 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20510 require_noerr( err, exit );
20511
20512 if( inRDataPtr )
20513 {
20514 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
20515 require_noerr( err, exit );
20516 }
20517
20518 exit:
20519 return( err );
20520 }
20521
20522 //===========================================================================================================================
20523 // _NanoTime64ToTimestamp
20524 //===========================================================================================================================
20525
20526 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
20527 {
20528 struct timeval tv;
20529
20530 NanoTimeToTimeVal( inTime, &tv );
20531 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
20532 }
20533
20534 //===========================================================================================================================
20535 // _MDNSInterfaceListCreate
20536 //===========================================================================================================================
20537
20538 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
20539
20540 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
20541 {
20542 OSStatus err;
20543 struct ifaddrs * ifaList;
20544 const struct ifaddrs * ifa;
20545 MDNSInterfaceItem * interfaceList;
20546 MDNSInterfaceItem ** ptr;
20547 SocketRef infoSock;
20548
20549 ifaList = NULL;
20550 interfaceList = NULL;
20551 infoSock = kInvalidSocketRef;
20552 if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
20553 require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
20554
20555 infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
20556 err = map_socket_creation_errno( infoSock );
20557 require_noerr( err, exit );
20558
20559 err = getifaddrs( &ifaList );
20560 err = map_global_noerr_errno( err );
20561 require_noerr( err, exit );
20562
20563 ptr = &interfaceList;
20564 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
20565 {
20566 MDNSInterfaceItem * item;
20567 int family;
20568 const unsigned int flagsMask = IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
20569 const unsigned int flagsNeeded = IFF_UP | IFF_MULTICAST;
20570
20571 if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded ) continue;
20572 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
20573 family = ifa->ifa_addr->sa_family;
20574 if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
20575
20576 for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
20577 if( !item )
20578 {
20579 NetTransportType type;
20580 uint32_t ifIndex;
20581 const char * const ifName = ifa->ifa_name;
20582
20583 if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
20584 err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
20585 require_noerr( err, exit );
20586
20587 if( ifIndex == 0 ) continue;
20588 if( type == kNetTransportType_AWDL )
20589 {
20590 if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
20591 }
20592 else
20593 {
20594 if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
20595 }
20596 item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
20597 require_action( item, exit, err = kNoMemoryErr );
20598
20599 *ptr = item;
20600 ptr = &item->next;
20601
20602 item->ifName = strdup( ifName );
20603 require_action( item->ifName, exit, err = kNoMemoryErr );
20604
20605 item->ifIndex = ifIndex;
20606 if( type == kNetTransportType_AWDL ) item->isAWDL = true;
20607 else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
20608 }
20609 if( family == AF_INET ) item->hasIPv4 = true;
20610 else item->hasIPv6 = true;
20611 }
20612 require_action_quiet( interfaceList, exit, err = kNotFoundErr );
20613
20614 if( outList )
20615 {
20616 *outList = interfaceList;
20617 interfaceList = NULL;
20618 }
20619
20620 exit:
20621 if( ifaList ) freeifaddrs( ifaList );
20622 _MDNSInterfaceListFree( interfaceList );
20623 ForgetSocket( &infoSock );
20624 return( err );
20625 }
20626
20627 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
20628 {
20629 OSStatus err;
20630 int i;
20631 static const char * const kMDNSInterfacePrefixBlacklist[] = { "llw" };
20632 struct ifreq ifr;
20633
20634 // Check if the interface name's prefix matches the prefix blacklist.
20635
20636 for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
20637 {
20638 const char * const prefix = kMDNSInterfacePrefixBlacklist[ i ];
20639
20640 if( strcmp_prefix( inIfName, prefix ) == 0 )
20641 {
20642 const char * ptr = &inIfName[ strlen( prefix ) ];
20643
20644 while( isdigit_safe( *ptr ) ) ++ptr;
20645 if( *ptr == '\0' ) return( true );
20646 }
20647 }
20648
20649 // Check if the interface is used for inter-(co)processor networking.
20650
20651 memset( &ifr, 0, sizeof( ifr ) );
20652 strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
20653 err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
20654 err = map_global_value_errno( err != -1, err );
20655 if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
20656
20657 return( false );
20658 }
20659
20660 //===========================================================================================================================
20661 // _MDNSInterfaceListFree
20662 //===========================================================================================================================
20663
20664 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList )
20665 {
20666 MDNSInterfaceItem * item;
20667
20668 while( ( item = inList ) != NULL )
20669 {
20670 inList = item->next;
20671 FreeNullSafe( item->ifName );
20672 free( item );
20673 }
20674 }
20675
20676 //===========================================================================================================================
20677 // _MDNSInterfaceGetAny
20678 //===========================================================================================================================
20679
20680 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
20681 {
20682 OSStatus err;
20683 MDNSInterfaceItem * list;
20684 const MDNSInterfaceItem * item;
20685
20686 list = NULL;
20687 err = _MDNSInterfaceListCreate( inSubset, 0, &list );
20688 require_noerr_quiet( err, exit );
20689 require_action_quiet( list, exit, err = kNotFoundErr );
20690
20691 for( item = list; item; item = item->next )
20692 {
20693 if( item->hasIPv4 && item->hasIPv6 ) break;
20694 }
20695 if( !item ) item = list;
20696 if( inNameBuf ) strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
20697 if( outIndex ) *outIndex = item->ifIndex;
20698
20699 exit:
20700 _MDNSInterfaceListFree( list );
20701 return( err );
20702 }
20703
20704 //===========================================================================================================================
20705 // _SetComputerName
20706 //===========================================================================================================================
20707
20708 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
20709 {
20710 OSStatus err;
20711 SCPreferencesRef prefs;
20712 Boolean ok;
20713
20714 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20715 err = map_scerror( prefs );
20716 require_noerr_quiet( err, exit );
20717
20718 ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
20719 err = map_scerror( ok );
20720 require_noerr_quiet( err, exit );
20721
20722 ok = SCPreferencesCommitChanges( prefs );
20723 err = map_scerror( ok );
20724 require_noerr_quiet( err, exit );
20725
20726 ok = SCPreferencesApplyChanges( prefs );
20727 err = map_scerror( ok );
20728 require_noerr_quiet( err, exit );
20729
20730 exit:
20731 CFReleaseNullSafe( prefs );
20732 return( err );
20733 }
20734
20735 //===========================================================================================================================
20736 // _SetComputerNameWithUTF8CString
20737 //===========================================================================================================================
20738
20739 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName )
20740 {
20741 OSStatus err;
20742 CFStringRef computerName;
20743
20744 computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
20745 require_action( computerName, exit, err = kNoMemoryErr );
20746
20747 err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
20748 require_noerr_quiet( err, exit );
20749
20750 exit:
20751 CFReleaseNullSafe( computerName );
20752 return( err );
20753 }
20754
20755 //===========================================================================================================================
20756 // _SetLocalHostName
20757 //===========================================================================================================================
20758
20759 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName )
20760 {
20761 OSStatus err;
20762 SCPreferencesRef prefs;
20763 Boolean ok;
20764
20765 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20766 err = map_scerror( prefs );
20767 require_noerr_quiet( err, exit );
20768
20769 ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
20770 err = map_scerror( ok );
20771 require_noerr_quiet( err, exit );
20772
20773 ok = SCPreferencesCommitChanges( prefs );
20774 err = map_scerror( ok );
20775 require_noerr_quiet( err, exit );
20776
20777 ok = SCPreferencesApplyChanges( prefs );
20778 err = map_scerror( ok );
20779 require_noerr_quiet( err, exit );
20780
20781 exit:
20782 CFReleaseNullSafe( prefs );
20783 return( err );
20784 }
20785
20786 //===========================================================================================================================
20787 // _SetLocalHostNameWithUTF8CString
20788 //===========================================================================================================================
20789
20790 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
20791 {
20792 OSStatus err;
20793 CFStringRef localHostName;
20794
20795 localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
20796 require_action( localHostName, exit, err = kNoMemoryErr );
20797
20798 err = _SetLocalHostName( localHostName );
20799 require_noerr_quiet( err, exit );
20800
20801 exit:
20802 CFReleaseNullSafe( localHostName );
20803 return( err );
20804 }
20805
20806 //===========================================================================================================================
20807 // MDNSColliderCreate
20808 //===========================================================================================================================
20809
20810 typedef enum
20811 {
20812 kMDNSColliderOpCode_Invalid = 0,
20813 kMDNSColliderOpCode_Send = 1,
20814 kMDNSColliderOpCode_Wait = 2,
20815 kMDNSColliderOpCode_SetProbeActions = 3,
20816 kMDNSColliderOpCode_LoopPush = 4,
20817 kMDNSColliderOpCode_LoopPop = 5,
20818 kMDNSColliderOpCode_Exit = 6
20819
20820 } MDNSColliderOpCode;
20821
20822 typedef struct
20823 {
20824 MDNSColliderOpCode opcode;
20825 uint32_t operand;
20826
20827 } MDNSCInstruction;
20828
20829 #define kMaxLoopDepth 16
20830
20831 struct MDNSColliderPrivate
20832 {
20833 CFRuntimeBase base; // CF object base.
20834 dispatch_queue_t queue; // Queue for collider's events.
20835 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
20836 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
20837 SocketRef sockV4; // IPv4 UDP socket for mDNS.
20838 SocketRef sockV6; // IPv6 UDP socket for mDNS.
20839 uint8_t * target; // Record name being targeted. (malloced)
20840 uint8_t * responsePtr; // Response message pointer. (malloced)
20841 size_t responseLen; // Response message length.
20842 uint8_t * probePtr; // Probe query message pointer. (malloced)
20843 size_t probeLen; // Probe query message length.
20844 unsigned int probeCount; // Count of probe queries received for collider's record.
20845 uint32_t probeActionMap; // Bitmap of actions to take for
20846 MDNSCInstruction * program; // Program to execute.
20847 uint32_t pc; // Program's program counter.
20848 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
20849 uint32_t loopDepth; // Current loop depth.
20850 dispatch_source_t waitTimer; // Timer for program's wait commands.
20851 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
20852 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
20853 void * stopContext; // User's stop handler context.
20854 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
20855 Boolean stopped; // True if the collider has been stopped.
20856 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
20857 };
20858
20859 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
20860 static void _MDNSColliderReadHandler( void *inContext );
20861 static void _MDNSColliderExecuteProgram( void *inContext );
20862 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20863 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20864
20865 CF_CLASS_DEFINE( MDNSCollider );
20866
20867 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
20868 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20869
20870 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
20871 {
20872 OSStatus err;
20873 MDNSColliderRef obj = NULL;
20874
20875 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
20876
20877 ReplaceDispatchQueue( &obj->queue, inQueue );
20878 obj->sockV4 = kInvalidSocketRef;
20879 obj->sockV6 = kInvalidSocketRef;
20880
20881 *outCollider = obj;
20882 err = kNoErr;
20883
20884 exit:
20885 return( err );
20886 }
20887
20888 //===========================================================================================================================
20889 // _MDNSColliderFinalize
20890 //===========================================================================================================================
20891
20892 static void _MDNSColliderFinalize( CFTypeRef inObj )
20893 {
20894 MDNSColliderRef const me = (MDNSColliderRef) inObj;
20895
20896 check( !me->waitTimer );
20897 check( !me->readSourceV4 );
20898 check( !me->readSourceV6 );
20899 check( !IsValidSocket( me->sockV4 ) );
20900 check( !IsValidSocket( me->sockV6 ) );
20901 ForgetMem( &me->target );
20902 ForgetMem( &me->responsePtr );
20903 ForgetMem( &me->probePtr );
20904 ForgetMem( &me->program );
20905 dispatch_forget( &me->queue );
20906 }
20907
20908 //===========================================================================================================================
20909 // MDNSColliderStart
20910 //===========================================================================================================================
20911
20912 static void _MDNSColliderStart( void *inContext );
20913
20914 static OSStatus MDNSColliderStart( MDNSColliderRef me )
20915 {
20916 OSStatus err;
20917
20918 require_action_quiet( me->target, exit, err = kNotPreparedErr );
20919 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
20920 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
20921 require_action_quiet( me->program, exit, err = kNotPreparedErr );
20922 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
20923 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
20924
20925 CFRetain( me );
20926 dispatch_async_f( me->queue, me, _MDNSColliderStart );
20927 err = kNoErr;
20928
20929 exit:
20930 return( err );
20931 }
20932
20933 static void _MDNSColliderStart( void *inContext )
20934 {
20935 OSStatus err;
20936 MDNSColliderRef const me = (MDNSColliderRef) inContext;
20937 SocketRef sock = kInvalidSocketRef;
20938 SocketContext * sockCtx = NULL;
20939
20940 if( me->protocols & kMDNSColliderProtocol_IPv4 )
20941 {
20942 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20943 require_noerr( err, exit );
20944
20945 err = SocketContextCreate( sock, me, &sockCtx );
20946 require_noerr( err, exit );
20947 sock = kInvalidSocketRef;
20948
20949 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
20950 sockCtx, &me->readSourceV4 );
20951 require_noerr( err, exit );
20952 me->sockV4 = sockCtx->sock;
20953 sockCtx = NULL;
20954
20955 dispatch_resume( me->readSourceV4 );
20956 }
20957
20958 if( me->protocols & kMDNSColliderProtocol_IPv6 )
20959 {
20960 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20961 require_noerr( err, exit );
20962
20963 err = SocketContextCreate( sock, me, &sockCtx );
20964 require_noerr( err, exit );
20965 sock = kInvalidSocketRef;
20966
20967 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
20968 sockCtx, &me->readSourceV6 );
20969 require_noerr( err, exit );
20970 me->sockV6 = sockCtx->sock;
20971 sockCtx = NULL;
20972
20973 dispatch_resume( me->readSourceV6 );
20974 }
20975
20976 _MDNSColliderExecuteProgram( me );
20977 err = kNoErr;
20978
20979 exit:
20980 ForgetSocket( &sock );
20981 ForgetSocketContext( &sockCtx );
20982 if( err ) _MDNSColliderStop( me, err );
20983 }
20984
20985 //===========================================================================================================================
20986 // MDNSColliderStop
20987 //===========================================================================================================================
20988
20989 static void _MDNSColliderUserStop( void *inContext );
20990
20991 static void MDNSColliderStop( MDNSColliderRef me )
20992 {
20993 CFRetain( me );
20994 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
20995 }
20996
20997 static void _MDNSColliderUserStop( void *inContext )
20998 {
20999 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21000
21001 _MDNSColliderStop( me, kCanceledErr );
21002 CFRelease( me );
21003 }
21004
21005 //===========================================================================================================================
21006 // MDNSColliderSetProtocols
21007 //===========================================================================================================================
21008
21009 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
21010 {
21011 me->protocols = inProtocols;
21012 }
21013
21014 //===========================================================================================================================
21015 // MDNSColliderSetInterfaceIndex
21016 //===========================================================================================================================
21017
21018 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
21019 {
21020 me->interfaceIndex = inInterfaceIndex;
21021 }
21022
21023 //===========================================================================================================================
21024 // MDNSColliderSetProgram
21025 //===========================================================================================================================
21026
21027 #define kMDNSColliderProgCmd_Done "done"
21028 #define kMDNSColliderProgCmd_Loop "loop"
21029 #define kMDNSColliderProgCmd_Send "send"
21030 #define kMDNSColliderProgCmd_Probes "probes"
21031 #define kMDNSColliderProgCmd_Wait "wait"
21032
21033 typedef uint32_t MDNSColliderProbeAction;
21034
21035 #define kMDNSColliderProbeAction_None 0
21036 #define kMDNSColliderProbeAction_Respond 1
21037 #define kMDNSColliderProbeAction_RespondUnicast 2
21038 #define kMDNSColliderProbeAction_RespondMulticast 3
21039 #define kMDNSColliderProbeAction_Probe 4
21040 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21041
21042 #define kMDNSColliderProbeActionBits_Count 3
21043 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21044 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21045
21046 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
21047
21048 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
21049
21050 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
21051 {
21052 OSStatus err;
21053 uint32_t insCount;
21054 unsigned int loopDepth;
21055 const char * cmd;
21056 const char * end;
21057 const char * next;
21058 MDNSCInstruction * program = NULL;
21059 uint32_t loopStart[ kMaxLoopDepth ];
21060
21061 insCount = 0;
21062 for( cmd = inProgramStr; *cmd; cmd = next )
21063 {
21064 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21065 require_action_quiet( end != cmd, exit, err = kMalformedErr );
21066 next = ( *end == ';' ) ? ( end + 1 ) : end;
21067 ++insCount;
21068 }
21069
21070 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
21071 require_action( program, exit, err = kNoMemoryErr );
21072
21073 insCount = 0;
21074 loopDepth = 0;
21075 for( cmd = inProgramStr; *cmd; cmd = next )
21076 {
21077 size_t cmdLen;
21078 const char * ptr;
21079 const char * arg;
21080 size_t argLen;
21081 uint32_t value;
21082 MDNSCInstruction * const ins = &program[ insCount ];
21083
21084 while( isspace_safe( *cmd ) ) ++cmd;
21085 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21086 next = ( *end == ';' ) ? ( end + 1 ) : end;
21087
21088 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21089 cmdLen = (size_t)( ptr - cmd );
21090
21091 // Done statement
21092
21093 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
21094 {
21095 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21096 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21097
21098 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
21099
21100 ins->opcode = kMDNSColliderOpCode_LoopPop;
21101 ins->operand = loopStart[ --loopDepth ];
21102 }
21103
21104 // Loop command
21105
21106 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
21107 {
21108 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21109 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21110 require_noerr_quiet( err, exit );
21111 require_action_quiet( value > 0, exit, err = kValueErr );
21112
21113 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21114 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21115
21116 ins->opcode = kMDNSColliderOpCode_LoopPush;
21117 ins->operand = value;
21118
21119 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
21120 loopStart[ loopDepth++ ] = insCount + 1;
21121 }
21122
21123 // Probes command
21124
21125 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
21126 {
21127 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21128 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21129 argLen = (size_t)( ptr - arg );
21130 if( argLen > 0 )
21131 {
21132 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
21133 require_noerr_quiet( err, exit );
21134 }
21135 else
21136 {
21137 value = 0;
21138 }
21139
21140 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21141 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21142
21143 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
21144 ins->operand = value;
21145 }
21146
21147 // Send command
21148
21149 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
21150 {
21151 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21152 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21153
21154 ins->opcode = kMDNSColliderOpCode_Send;
21155 }
21156
21157 // Wait command
21158
21159 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
21160 {
21161 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21162 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21163 require_noerr_quiet( err, exit );
21164
21165 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21166 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21167
21168 ins->opcode = kMDNSColliderOpCode_Wait;
21169 ins->operand = value;
21170 }
21171
21172 // Unrecognized command
21173
21174 else
21175 {
21176 err = kCommandErr;
21177 goto exit;
21178 }
21179 ++insCount;
21180 }
21181 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
21182
21183 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
21184
21185 FreeNullSafe( me->program );
21186 me->program = program;
21187 program = NULL;
21188 err = kNoErr;
21189
21190 exit:
21191 FreeNullSafe( program );
21192 return( err );
21193 }
21194
21195 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
21196 {
21197 OSStatus err;
21198 const char * ptr;
21199 const char * const end = &inString[ inLen ];
21200 uint32_t bitmap;
21201 int index;
21202
21203 bitmap = 0;
21204 index = 0;
21205 ptr = inString;
21206 while( ptr < end )
21207 {
21208 int c, count;
21209 MDNSColliderProbeAction action;
21210
21211 c = *ptr++;
21212 if( isdigit_safe( c ) )
21213 {
21214 count = 0;
21215 do
21216 {
21217 count = ( count * 10 ) + ( c - '0' );
21218 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
21219 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
21220 c = *ptr++;
21221
21222 } while( isdigit_safe( c ) );
21223 require_action_quiet( count > 0, exit, err = kCountErr );
21224 }
21225 else
21226 {
21227 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
21228 count = 1;
21229 }
21230
21231 switch( c )
21232 {
21233 case 'n': action = kMDNSColliderProbeAction_None; break;
21234 case 'r': action = kMDNSColliderProbeAction_Respond; break;
21235 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
21236 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
21237 case 'p': action = kMDNSColliderProbeAction_Probe; break;
21238 default: err = kMalformedErr; goto exit;
21239 }
21240 if( ptr < end )
21241 {
21242 c = *ptr++;
21243 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
21244 }
21245 while( count-- > 0 )
21246 {
21247 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
21248 ++index;
21249 }
21250 }
21251
21252 *outBitmap = bitmap;
21253 err = kNoErr;
21254
21255 exit:
21256 return( err );
21257 }
21258
21259 //===========================================================================================================================
21260 // MDNSColliderSetStopHandler
21261 //===========================================================================================================================
21262
21263 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
21264 {
21265 me->stopHandler = inStopHandler;
21266 me->stopContext = inStopContext;
21267 }
21268
21269 //===========================================================================================================================
21270 // MDNSColliderSetRecord
21271 //===========================================================================================================================
21272
21273 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21274 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21275 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21276
21277 static OSStatus
21278 MDNSColliderSetRecord(
21279 MDNSColliderRef me,
21280 const uint8_t * inName,
21281 uint16_t inType,
21282 const void * inRDataPtr,
21283 size_t inRDataLen )
21284 {
21285 OSStatus err;
21286 DataBuffer msgDB;
21287 DNSHeader header;
21288 uint8_t * targetPtr = NULL;
21289 size_t targetLen;
21290 uint8_t * responsePtr = NULL;
21291 size_t responseLen;
21292 uint8_t * probePtr = NULL;
21293 size_t probeLen;
21294
21295 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
21296
21297 err = DomainNameDup( inName, &targetPtr, &targetLen );
21298 require_noerr_quiet( err, exit );
21299
21300 // Create response message.
21301
21302 memset( &header, 0, sizeof( header ) );
21303 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
21304 DNSHeaderSetAnswerCount( &header, 1 );
21305
21306 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21307 require_noerr( err, exit );
21308
21309 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kRRClassCacheFlushBit,
21310 1976, inRDataPtr, inRDataLen );
21311 require_noerr( err, exit );
21312
21313 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
21314 require_noerr( err, exit );
21315
21316 // Create probe message.
21317
21318 memset( &header, 0, sizeof( header ) );
21319 DNSHeaderSetQuestionCount( &header, 2 );
21320 DNSHeaderSetAuthorityCount( &header, 1 );
21321
21322 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21323 require_noerr( err, exit );
21324
21325 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
21326 require_noerr( err, exit );
21327
21328 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
21329 kDNSServiceType_NULL, kDNSServiceClass_IN );
21330 require_noerr( err, exit );
21331
21332 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
21333 1976, inRDataPtr, inRDataLen );
21334 require_noerr( err, exit );
21335
21336 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
21337 require_noerr( err, exit );
21338
21339 FreeNullSafe( me->target );
21340 me->target = targetPtr;
21341 targetPtr = NULL;
21342
21343 FreeNullSafe( me->responsePtr );
21344 me->responsePtr = responsePtr;
21345 me->responseLen = responseLen;
21346 responsePtr = NULL;
21347
21348 FreeNullSafe( me->probePtr );
21349 me->probePtr = probePtr;
21350 me->probeLen = probeLen;
21351 probePtr = NULL;
21352
21353 exit:
21354 DataBuffer_Free( &msgDB );
21355 FreeNullSafe( targetPtr );
21356 FreeNullSafe( responsePtr );
21357 FreeNullSafe( probePtr );
21358 return( err );
21359 }
21360
21361 //===========================================================================================================================
21362 // _MDNSColliderStop
21363 //===========================================================================================================================
21364
21365 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
21366 {
21367 dispatch_source_forget( &me->waitTimer );
21368 dispatch_source_forget( &me->readSourceV4 );
21369 dispatch_source_forget( &me->readSourceV6 );
21370 me->sockV4 = kInvalidSocketRef;
21371 me->sockV6 = kInvalidSocketRef;
21372
21373 if( !me->stopped )
21374 {
21375 me->stopped = true;
21376 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
21377 CFRelease( me );
21378 }
21379 }
21380
21381 //===========================================================================================================================
21382 // _MDNSColliderReadHandler
21383 //===========================================================================================================================
21384
21385 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
21386 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
21387
21388 static void _MDNSColliderReadHandler( void *inContext )
21389 {
21390 OSStatus err;
21391 struct timeval now;
21392 SocketContext * const sockCtx = (SocketContext *) inContext;
21393 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
21394 size_t msgLen;
21395 sockaddr_ip sender;
21396 const DNSHeader * hdr;
21397 const uint8_t * ptr;
21398 const struct sockaddr * dest;
21399 int probeFound, probeIsQU;
21400 unsigned int qCount, i;
21401 MDNSColliderProbeAction action;
21402
21403 gettimeofday( &now, NULL );
21404
21405 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
21406 NULL, NULL, NULL, NULL );
21407 require_noerr( err, exit );
21408
21409 require_quiet( msgLen >= kDNSHeaderLength, exit );
21410 hdr = (const DNSHeader *) me->msgBuf;
21411
21412 probeFound = false;
21413 probeIsQU = false;
21414 qCount = DNSHeaderGetQuestionCount( hdr );
21415 ptr = (const uint8_t *) &hdr[ 1 ];
21416 for( i = 0; i < qCount; ++i )
21417 {
21418 uint16_t qtype, qclass;
21419 uint8_t qname[ kDomainNameLengthMax ];
21420
21421 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
21422 require_noerr_quiet( err, exit );
21423
21424 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
21425 DomainNameEqual( qname, kMDNSColliderDummyName ) )
21426 {
21427 probeFound = false;
21428 break;
21429 }
21430
21431 if( qtype != kDNSServiceType_ANY ) continue;
21432 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
21433 if( !DomainNameEqual( qname, me->target ) ) continue;
21434
21435 if( !probeFound )
21436 {
21437 probeFound = true;
21438 probeIsQU = ( qclass & kQClassUnicastResponseBit ) ? true : false;
21439 }
21440 }
21441 require_quiet( probeFound, exit );
21442
21443 ++me->probeCount;
21444 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
21445
21446 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21447 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
21448
21449 if( ( action == kMDNSColliderProbeAction_Respond ) ||
21450 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
21451 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21452 {
21453 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
21454 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
21455 {
21456 dest = &sender.sa;
21457 }
21458 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
21459 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21460 {
21461 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21462 }
21463
21464 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
21465 require_noerr( err, exit );
21466 }
21467 else if( action == kMDNSColliderProbeAction_Probe )
21468 {
21469 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21470
21471 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
21472 require_noerr( err, exit );
21473 }
21474
21475 exit:
21476 return;
21477 }
21478
21479 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
21480 {
21481 MDNSColliderProbeAction action;
21482
21483 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
21484 {
21485 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
21486 kMDNSColliderProbeActionBits_Mask;
21487 }
21488 else
21489 {
21490 action = kMDNSColliderProbeAction_None;
21491 }
21492 return( action );
21493 }
21494
21495 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
21496 {
21497 switch( inAction )
21498 {
21499 case kMDNSColliderProbeAction_None: return( "None" );
21500 case kMDNSColliderProbeAction_Respond: return( "Respond" );
21501 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
21502 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
21503 case kMDNSColliderProbeAction_Probe: return( "Probe" );
21504 default: return( "???" );
21505 }
21506 }
21507
21508 //===========================================================================================================================
21509 // _MDNSColliderExecuteProgram
21510 //===========================================================================================================================
21511
21512 static void _MDNSColliderExecuteProgram( void *inContext )
21513 {
21514 OSStatus err;
21515 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21516 int stop;
21517
21518 dispatch_forget( &me->waitTimer );
21519
21520 stop = false;
21521 for( ;; )
21522 {
21523 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
21524 uint32_t waitMs;
21525
21526 switch( ins->opcode )
21527 {
21528 case kMDNSColliderOpCode_Send:
21529 if( IsValidSocket( me->sockV4 ) )
21530 {
21531 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
21532 require_noerr( err, exit );
21533 }
21534 if( IsValidSocket( me->sockV6 ) )
21535 {
21536 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
21537 require_noerr( err, exit );
21538 }
21539 break;
21540
21541 case kMDNSColliderOpCode_Wait:
21542 waitMs = ins->operand;
21543 if( waitMs > 0 )
21544 {
21545 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
21546 _MDNSColliderExecuteProgram, me, &me->waitTimer );
21547 require_noerr( err, exit );
21548 dispatch_resume( me->waitTimer );
21549 goto exit;
21550 }
21551 break;
21552
21553 case kMDNSColliderOpCode_SetProbeActions:
21554 me->probeCount = 0;
21555 me->probeActionMap = ins->operand;
21556 break;
21557
21558 case kMDNSColliderOpCode_LoopPush:
21559 check( me->loopDepth < kMaxLoopDepth );
21560 me->loopCounts[ me->loopDepth++ ] = ins->operand;
21561 break;
21562
21563 case kMDNSColliderOpCode_LoopPop:
21564 check( me->loopDepth > 0 );
21565 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
21566 {
21567 me->pc = ins->operand;
21568 }
21569 else
21570 {
21571 --me->loopDepth;
21572 }
21573 break;
21574
21575 case kMDNSColliderOpCode_Exit:
21576 stop = true;
21577 err = kNoErr;
21578 goto exit;
21579
21580 default:
21581 dlogassert( "Unhandled opcode %u\n", ins->opcode );
21582 err = kCommandErr;
21583 goto exit;
21584 }
21585 }
21586
21587 exit:
21588 if( err || stop ) _MDNSColliderStop( me, err );
21589 }
21590
21591 //===========================================================================================================================
21592 // _MDNSColliderSendResponse
21593 //===========================================================================================================================
21594
21595 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21596 {
21597 OSStatus err;
21598 ssize_t n;
21599
21600 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
21601 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
21602 return( err );
21603 }
21604
21605 //===========================================================================================================================
21606 // _MDNSColliderSendProbe
21607 //===========================================================================================================================
21608
21609 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21610 {
21611 OSStatus err;
21612 ssize_t n;
21613
21614 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
21615 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
21616 return( err );
21617 }
21618
21619 //===========================================================================================================================
21620 // ServiceBrowserCreate
21621 //===========================================================================================================================
21622
21623 typedef struct SBDomain SBDomain;
21624 typedef struct SBServiceType SBServiceType;
21625 typedef struct SBServiceBrowse SBServiceBrowse;
21626 typedef struct SBServiceInstance SBServiceInstance;
21627 typedef struct SBIPAddress SBIPAddress;
21628
21629 struct ServiceBrowserPrivate
21630 {
21631 CFRuntimeBase base; // CF object base.
21632 dispatch_queue_t queue; // Queue for service browser's events.
21633 DNSServiceRef connection; // Shared connection for DNS-SD ops.
21634 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
21635 char * domain; // If non-null, then browsing is limited to this domain.
21636 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
21637 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
21638 void * userContext; // User's callback context.
21639 SBDomain * domainList; // List of domains and their browse results.
21640 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
21641 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
21642 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
21643 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21644 // use the "any" interface.
21645 };
21646
21647 struct SBDomain
21648 {
21649 SBDomain * next; // Next domain object in list.
21650 ServiceBrowserRef browser; // Pointer to parent service browser.
21651 char * name; // Name of the domain.
21652 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21653 SBServiceType * typeList; // List of service types to browse for in this domain.
21654 };
21655
21656 struct SBServiceType
21657 {
21658 SBServiceType * next; // Next service type object in list.
21659 char * name; // Name of the service type.
21660 SBServiceBrowse * browseList; // List of browses for this service type.
21661 };
21662
21663 struct SBServiceBrowse
21664 {
21665 SBServiceBrowse * next; // Next browse object in list.
21666 ServiceBrowserRef browser; // Pointer to parent service browser.
21667 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
21668 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
21669 uint64_t startTicks; // Value of UpTicks() when the browse op began.
21670 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
21671 };
21672
21673 struct SBServiceInstance
21674 {
21675 SBServiceInstance * next; // Next service instance object in list.
21676 ServiceBrowserRef browser; // Pointer to parent service browser.
21677 char * name; // Name of the service instance.
21678 char * fqdn; // Fully qualified domain name of service instance (for logging/debugging).
21679 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
21680 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
21681 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
21682 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
21683 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
21684 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
21685 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
21686 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
21687 size_t txtLen; // Length of service instance's TXT record data.
21688 DNSServiceRef getAddrInfo; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21689 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21690 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
21691 };
21692
21693 struct SBIPAddress
21694 {
21695 SBIPAddress * next; // Next IP address object in list.
21696 sockaddr_ip sip; // IPv4 or IPv6 address.
21697 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
21698 };
21699
21700 typedef struct
21701 {
21702 SBRDomain * domainList; // List of domains in which services were found.
21703 int32_t refCount; // This object's reference count.
21704
21705 } ServiceBrowserResultsPrivate;
21706
21707 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
21708 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
21709 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
21710 static void _ServiceBrowserTimerHandler( void *inContext );
21711 static void DNSSD_API
21712 _ServiceBrowserDomainsQueryCallback(
21713 DNSServiceRef inSDRef,
21714 DNSServiceFlags inFlags,
21715 uint32_t inInterfaceIndex,
21716 DNSServiceErrorType inError,
21717 const char * inFullName,
21718 uint16_t inType,
21719 uint16_t inClass,
21720 uint16_t inRDataLen,
21721 const void * inRDataPtr,
21722 uint32_t inTTL,
21723 void * inContext );
21724 static void DNSSD_API
21725 _ServiceBrowserServicesQueryCallback(
21726 DNSServiceRef inSDRef,
21727 DNSServiceFlags inFlags,
21728 uint32_t inInterfaceIndex,
21729 DNSServiceErrorType inError,
21730 const char * inFullName,
21731 uint16_t inType,
21732 uint16_t inClass,
21733 uint16_t inRDataLen,
21734 const void * inRDataPtr,
21735 uint32_t inTTL,
21736 void * inContext );
21737 static void DNSSD_API
21738 _ServiceBrowserBrowseCallback(
21739 DNSServiceRef inSDRef,
21740 DNSServiceFlags inFlags,
21741 uint32_t inInterfaceIndex,
21742 DNSServiceErrorType inError,
21743 const char * inName,
21744 const char * inRegType,
21745 const char * inDomain,
21746 void * inContext );
21747 static void DNSSD_API
21748 _ServiceBrowserResolveCallback(
21749 DNSServiceRef inSDRef,
21750 DNSServiceFlags inFlags,
21751 uint32_t inInterfaceIndex,
21752 DNSServiceErrorType inError,
21753 const char * inFullName,
21754 const char * inHostname,
21755 uint16_t inPort,
21756 uint16_t inTXTLen,
21757 const unsigned char * inTXTPtr,
21758 void * inContext );
21759 static void DNSSD_API
21760 _ServiceBrowserGAICallback(
21761 DNSServiceRef inSDRef,
21762 DNSServiceFlags inFlags,
21763 uint32_t inInterfaceIndex,
21764 DNSServiceErrorType inError,
21765 const char * inHostname,
21766 const struct sockaddr * inSockAddr,
21767 uint32_t inTTL,
21768 void * inContext );
21769 static OSStatus
21770 _ServiceBrowserAddServiceType(
21771 ServiceBrowserRef inBrowser,
21772 SBDomain * inDomain,
21773 const char * inName,
21774 uint32_t inIfIndex );
21775 static OSStatus
21776 _ServiceBrowserRemoveServiceType(
21777 ServiceBrowserRef inBrowser,
21778 SBDomain * inDomain,
21779 const char * inName,
21780 uint32_t inIfIndex );
21781 static OSStatus
21782 _ServiceBrowserAddServiceInstance(
21783 ServiceBrowserRef inBrowser,
21784 SBServiceBrowse * inBrowse,
21785 uint32_t inIfIndex,
21786 const char * inName,
21787 const char * inRegType,
21788 const char * inDomain,
21789 uint64_t inDiscoverTimeUs );
21790 static OSStatus
21791 _ServiceBrowserRemoveServiceInstance(
21792 ServiceBrowserRef inBrowser,
21793 SBServiceBrowse * inBrowse,
21794 const char * inName,
21795 uint32_t inIfIndex );
21796 static OSStatus
21797 _ServiceBrowserAddIPAddress(
21798 ServiceBrowserRef inBrowser,
21799 SBServiceInstance * inInstance,
21800 const struct sockaddr * inSockAddr,
21801 uint64_t inResolveTimeUs );
21802 static OSStatus
21803 _ServiceBrowserRemoveIPAddress(
21804 ServiceBrowserRef inBrowser,
21805 SBServiceInstance * inInstance,
21806 const struct sockaddr * inSockAddr );
21807 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
21808 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
21809 static void _SBDomainFree( SBDomain *inDomain );
21810 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
21811 static void _SBServiceTypeFree( SBServiceType *inType );
21812 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
21813 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
21814 static OSStatus
21815 _SBServiceInstanceCreate(
21816 const char * inName,
21817 const char * inType,
21818 const char * inDomain,
21819 uint32_t inIfIndex,
21820 uint64_t inDiscoverTimeUs,
21821 ServiceBrowserRef inBrowser,
21822 SBServiceInstance ** outInstance );
21823 static void _SBServiceInstanceFree( SBServiceInstance *inInstance );
21824 static OSStatus
21825 _SBIPAddressCreate(
21826 const struct sockaddr * inSockAddr,
21827 uint64_t inResolveTimeUs,
21828 SBIPAddress ** outIPAddress );
21829 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
21830 static void _SBIPAddressFreeList( SBIPAddress *inList );
21831 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
21832 static void _SBRDomainFree( SBRDomain *inDomain );
21833 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
21834 static void _SBRServiceTypeFree( SBRServiceType *inType );
21835 static OSStatus
21836 _SBRServiceInstanceCreate(
21837 const char * inName,
21838 uint32_t inInterfaceIndex,
21839 const char * inHostname,
21840 uint16_t inPort,
21841 const uint8_t * inTXTPtr,
21842 size_t inTXTLen,
21843 uint64_t inDiscoverTimeUs,
21844 uint64_t inResolveTimeUs,
21845 SBRServiceInstance ** outInstance );
21846 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
21847 static OSStatus
21848 _SBRIPAddressCreate(
21849 const struct sockaddr * inSockAddr,
21850 uint64_t inResolveTimeUs,
21851 SBRIPAddress ** outIPAddress );
21852 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
21853
21854 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21855
21856 CF_CLASS_DEFINE( ServiceBrowser );
21857
21858 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
21859 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21860
21861 static OSStatus
21862 ServiceBrowserCreate(
21863 dispatch_queue_t inQueue,
21864 uint32_t inInterfaceIndex,
21865 const char * inDomain,
21866 unsigned int inBrowseTimeSecs,
21867 Boolean inIncludeAWDL,
21868 ServiceBrowserRef * outBrowser )
21869 {
21870 OSStatus err;
21871 ServiceBrowserRef obj;
21872
21873 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
21874
21875 ReplaceDispatchQueue( &obj->queue, inQueue );
21876 obj->ifIndex = inInterfaceIndex;
21877 if( inDomain )
21878 {
21879 obj->domain = strdup( inDomain );
21880 require_action( obj->domain, exit, err = kNoMemoryErr );
21881 }
21882 obj->browseTimeSecs = inBrowseTimeSecs;
21883 obj->includeAWDL = inIncludeAWDL;
21884
21885 *outBrowser = obj;
21886 obj = NULL;
21887 err = kNoErr;
21888
21889 exit:
21890 CFReleaseNullSafe( obj );
21891 return( err );
21892 }
21893
21894 //===========================================================================================================================
21895 // _ServiceBrowserFinalize
21896 //===========================================================================================================================
21897
21898 static void _ServiceBrowserFinalize( CFTypeRef inObj )
21899 {
21900 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
21901 StringListItem * serviceType;
21902
21903 dispatch_forget( &me->queue );
21904 check( !me->connection );
21905 check( !me->domainsQuery );
21906 ForgetMem( &me->domain );
21907 while( ( serviceType = me->serviceTypeList ) != NULL )
21908 {
21909 me->serviceTypeList = serviceType->next;
21910 ForgetMem( &serviceType->str );
21911 free( serviceType );
21912 }
21913 check( !me->domainList );
21914 check( !me->stopTimer );
21915 }
21916
21917 //===========================================================================================================================
21918 // ServiceBrowserStart
21919 //===========================================================================================================================
21920
21921 static void _ServiceBrowserStart( void *inContext );
21922
21923 static void ServiceBrowserStart( ServiceBrowserRef me )
21924 {
21925 CFRetain( me );
21926 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
21927 }
21928
21929 static void _ServiceBrowserStart( void *inContext )
21930 {
21931 OSStatus err;
21932 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
21933
21934 err = DNSServiceCreateConnection( &me->connection );
21935 require_noerr( err, exit );
21936
21937 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
21938 require_noerr( err, exit );
21939
21940 if( me->domain )
21941 {
21942 err = _ServiceBrowserAddDomain( me, me->domain );
21943 require_noerr( err, exit );
21944 }
21945 else
21946 {
21947 DNSServiceRef sdRef;
21948 const char * const recordName = "b._dns-sd._udp.local.";
21949 const uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
21950
21951 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21952 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21953
21954 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
21955
21956 sdRef = me->connection;
21957 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
21958 kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
21959 require_noerr( err, exit );
21960
21961 me->domainsQuery = sdRef;
21962 }
21963
21964 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
21965 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
21966 require_noerr( err, exit );
21967 dispatch_resume( me->stopTimer );
21968
21969 exit:
21970 if( err ) _ServiceBrowserStop( me, err );
21971 }
21972
21973 //===========================================================================================================================
21974 // ServiceBrowserAddServiceType
21975 //===========================================================================================================================
21976
21977 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
21978 {
21979 OSStatus err;
21980 StringListItem * item;
21981 StringListItem ** itemPtr;
21982 StringListItem * newItem = NULL;
21983
21984 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
21985 {
21986 if( strcmp( item->str, inServiceType ) == 0 ) break;
21987 }
21988 if( !item )
21989 {
21990 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
21991 require_action( newItem, exit, err = kNoMemoryErr );
21992
21993 newItem->str = strdup( inServiceType );
21994 require_action( newItem->str, exit, err = kNoMemoryErr );
21995
21996 *itemPtr = newItem;
21997 newItem = NULL;
21998 }
21999 err = kNoErr;
22000
22001 exit:
22002 FreeNullSafe( newItem );
22003 return( err );
22004 }
22005
22006 //===========================================================================================================================
22007 // ServiceBrowserSetCallback
22008 //===========================================================================================================================
22009
22010 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
22011 {
22012 me->userCallback = inCallback;
22013 me->userContext = inContext;
22014 }
22015
22016 //===========================================================================================================================
22017 // ServiceBrowserResultsRetain
22018 //===========================================================================================================================
22019
22020 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
22021 {
22022 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22023
22024 atomic_add_32( &results->refCount, 1 );
22025 }
22026
22027 //===========================================================================================================================
22028 // ServiceBrowserResultsRelease
22029 //===========================================================================================================================
22030
22031 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
22032 {
22033 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22034 SBRDomain * domain;
22035
22036 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
22037 {
22038 while( ( domain = inResults->domainList ) != NULL )
22039 {
22040 inResults->domainList = domain->next;
22041 _SBRDomainFree( domain );
22042 }
22043 free( inResults );
22044 }
22045 }
22046
22047 //===========================================================================================================================
22048 // _ServiceBrowserStop
22049 //===========================================================================================================================
22050
22051 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
22052 {
22053 OSStatus err;
22054 SBDomain * d;
22055 SBServiceType * t;
22056 SBServiceBrowse * b;
22057 SBServiceInstance * i;
22058
22059 dispatch_source_forget( &me->stopTimer );
22060 DNSServiceForget( &me->domainsQuery );
22061 for( d = me->domainList; d; d = d->next )
22062 {
22063 DNSServiceForget( &d->servicesQuery );
22064 for( t = d->typeList; t; t = t->next )
22065 {
22066 for( b = t->browseList; b; b = b->next )
22067 {
22068 DNSServiceForget( &b->browse );
22069 for( i = b->instanceList; i; i = i->next )
22070 {
22071 DNSServiceForget( &i->resolve );
22072 DNSServiceForget( &i->getAddrInfo );
22073 }
22074 }
22075 }
22076 }
22077 DNSServiceForget( &me->connection );
22078
22079 if( me->userCallback )
22080 {
22081 ServiceBrowserResults * results = NULL;
22082
22083 err = _ServiceBrowserCreateResults( me, &results );
22084 if( !err ) err = inError;
22085
22086 me->userCallback( results, err, me->userContext );
22087 me->userCallback = NULL;
22088 me->userContext = NULL;
22089 if( results ) ServiceBrowserResultsRelease( results );
22090 }
22091
22092 while( ( d = me->domainList ) != NULL )
22093 {
22094 me->domainList = d->next;
22095 _SBDomainFree( d );
22096 }
22097 CFRelease( me );
22098 }
22099
22100 //===========================================================================================================================
22101 // _ServiceBrowserAddDomain
22102 //===========================================================================================================================
22103
22104 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
22105 {
22106 OSStatus err;
22107 SBDomain * domain;
22108 SBDomain ** domainPtr;
22109 SBDomain * newDomain = NULL;
22110
22111 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22112 {
22113 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
22114 }
22115 require_action_quiet( !domain, exit, err = kDuplicateErr );
22116
22117 err = _SBDomainCreate( inDomain, me, &newDomain );
22118 require_noerr_quiet( err, exit );
22119
22120 if( me->serviceTypeList )
22121 {
22122 const StringListItem * item;
22123
22124 for( item = me->serviceTypeList; item; item = item->next )
22125 {
22126 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
22127 if( err == kDuplicateErr ) err = kNoErr;
22128 require_noerr( err, exit );
22129 }
22130 }
22131 else
22132 {
22133 char * recordName;
22134 DNSServiceRef sdRef;
22135 DNSServiceFlags flags;
22136
22137 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22138 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22139
22140 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
22141 require_action( recordName, exit, err = kNoMemoryErr );
22142
22143 flags = kDNSServiceFlagsShareConnection;
22144 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22145
22146 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
22147
22148 sdRef = newDomain->browser->connection;
22149 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
22150 _ServiceBrowserServicesQueryCallback, newDomain );
22151 free( recordName );
22152 require_noerr( err, exit );
22153
22154 newDomain->servicesQuery = sdRef;
22155 }
22156
22157 *domainPtr = newDomain;
22158 newDomain = NULL;
22159 err = kNoErr;
22160
22161 exit:
22162 if( newDomain ) _SBDomainFree( newDomain );
22163 return( err );
22164 }
22165
22166 //===========================================================================================================================
22167 // _ServiceBrowserRemoveDomain
22168 //===========================================================================================================================
22169
22170 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
22171 {
22172 OSStatus err;
22173 SBDomain * domain;
22174 SBDomain ** domainPtr;
22175
22176 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22177 {
22178 if( strcasecmp( domain->name, inName ) == 0 ) break;
22179 }
22180
22181 if( domain )
22182 {
22183 *domainPtr = domain->next;
22184 _SBDomainFree( domain );
22185 err = kNoErr;
22186 }
22187 else
22188 {
22189 err = kNotFoundErr;
22190 }
22191
22192 return( err );
22193 }
22194
22195 //===========================================================================================================================
22196 // _ServiceBrowserTimerHandler
22197 //===========================================================================================================================
22198
22199 static void _ServiceBrowserTimerHandler( void *inContext )
22200 {
22201 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22202
22203 _ServiceBrowserStop( me, kNoErr );
22204 }
22205
22206 //===========================================================================================================================
22207 // _ServiceBrowserDomainsQueryCallback
22208 //===========================================================================================================================
22209
22210 static void DNSSD_API
22211 _ServiceBrowserDomainsQueryCallback(
22212 DNSServiceRef inSDRef,
22213 DNSServiceFlags inFlags,
22214 uint32_t inInterfaceIndex,
22215 DNSServiceErrorType inError,
22216 const char * inFullName,
22217 uint16_t inType,
22218 uint16_t inClass,
22219 uint16_t inRDataLen,
22220 const void * inRDataPtr,
22221 uint32_t inTTL,
22222 void * inContext )
22223 {
22224 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22225 OSStatus err;
22226 char domainStr[ kDNSServiceMaxDomainName ];
22227
22228 Unused( inSDRef );
22229 Unused( inClass );
22230 Unused( inTTL );
22231
22232 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22233 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22234 !inError, inError );
22235
22236 require_noerr( inError, exit );
22237
22238 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
22239 require_noerr( err, exit );
22240
22241 if( inFlags & kDNSServiceFlagsAdd )
22242 {
22243 err = _ServiceBrowserAddDomain( me, domainStr );
22244 if( err == kDuplicateErr ) err = kNoErr;
22245 require_noerr( err, exit );
22246 }
22247 else
22248 {
22249 err = _ServiceBrowserRemoveDomain( me, domainStr );
22250 if( err == kNotFoundErr ) err = kNoErr;
22251 require_noerr( err, exit );
22252 }
22253
22254 exit:
22255 return;
22256 }
22257
22258 //===========================================================================================================================
22259 // _ServiceBrowserServicesQueryCallback
22260 //===========================================================================================================================
22261
22262 static void DNSSD_API
22263 _ServiceBrowserServicesQueryCallback(
22264 DNSServiceRef inSDRef,
22265 DNSServiceFlags inFlags,
22266 uint32_t inInterfaceIndex,
22267 DNSServiceErrorType inError,
22268 const char * inFullName,
22269 uint16_t inType,
22270 uint16_t inClass,
22271 uint16_t inRDataLen,
22272 const void * inRDataPtr,
22273 uint32_t inTTL,
22274 void * inContext )
22275 {
22276 OSStatus err;
22277 SBDomain * const domain = (SBDomain *) inContext;
22278 ServiceBrowserRef const me = domain->browser;
22279 const uint8_t * src;
22280 const uint8_t * end;
22281 uint8_t * dst;
22282 int i;
22283 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
22284 char serviceTypeStr[ kDNSServiceMaxDomainName ];
22285
22286 Unused( inSDRef );
22287 Unused( inTTL );
22288 Unused( inClass );
22289
22290 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22291 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22292 !inError, inError );
22293
22294 require_noerr( inError, exit );
22295
22296 check( inType == kDNSServiceType_PTR );
22297 check( inClass == kDNSServiceClass_IN );
22298
22299 // The first two labels of the domain name in the RDATA describe a service type.
22300 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22301
22302 src = (const uint8_t *) inRDataPtr;
22303 end = src + inRDataLen;
22304 dst = serviceType;
22305 for( i = 0; i < 2; ++i )
22306 {
22307 size_t labelLen;
22308
22309 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
22310
22311 labelLen = *src;
22312 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
22313 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
22314
22315 memcpy( dst, src, 1 + labelLen );
22316 src += 1 + labelLen;
22317 dst += 1 + labelLen;
22318 }
22319 *dst = 0;
22320
22321 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
22322 require_noerr( err, exit );
22323
22324 if( inFlags & kDNSServiceFlagsAdd )
22325 {
22326 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22327 if( err == kDuplicateErr ) err = kNoErr;
22328 require_noerr( err, exit );
22329 }
22330 else
22331 {
22332 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22333 if( err == kNotFoundErr ) err = kNoErr;
22334 require_noerr( err, exit );
22335 }
22336
22337 exit:
22338 return;
22339 }
22340
22341 //===========================================================================================================================
22342 // _ServiceBrowserBrowseCallback
22343 //===========================================================================================================================
22344
22345 static void DNSSD_API
22346 _ServiceBrowserBrowseCallback(
22347 DNSServiceRef inSDRef,
22348 DNSServiceFlags inFlags,
22349 uint32_t inInterfaceIndex,
22350 DNSServiceErrorType inError,
22351 const char * inName,
22352 const char * inRegType,
22353 const char * inDomain,
22354 void * inContext )
22355 {
22356 OSStatus err;
22357 const uint64_t nowTicks = UpTicks();
22358 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
22359 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
22360
22361 Unused( inSDRef );
22362
22363 sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22364 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
22365
22366 require_noerr( inError, exit );
22367
22368 if( inFlags & kDNSServiceFlagsAdd )
22369 {
22370 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
22371 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
22372 if( err == kDuplicateErr ) err = kNoErr;
22373 require_noerr( err, exit );
22374 }
22375 else
22376 {
22377 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
22378 if( err == kNotFoundErr ) err = kNoErr;
22379 require_noerr( err, exit );
22380 }
22381
22382 exit:
22383 return;
22384 }
22385
22386 //===========================================================================================================================
22387 // _ServiceBrowserResolveCallback
22388 //===========================================================================================================================
22389
22390 static void DNSSD_API
22391 _ServiceBrowserResolveCallback(
22392 DNSServiceRef inSDRef,
22393 DNSServiceFlags inFlags,
22394 uint32_t inInterfaceIndex,
22395 DNSServiceErrorType inError,
22396 const char * inFullName,
22397 const char * inHostname,
22398 uint16_t inPort,
22399 uint16_t inTXTLen,
22400 const unsigned char * inTXTPtr,
22401 void * inContext )
22402 {
22403 OSStatus err;
22404 const uint64_t nowTicks = UpTicks();
22405 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22406 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22407
22408 Unused( inSDRef );
22409 Unused( inFlags );
22410
22411 sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22412 (int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
22413
22414 require_noerr( inError, exit );
22415
22416 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
22417 {
22418 FreeNullSafe( instance->txtPtr );
22419 instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
22420 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
22421
22422 instance->txtLen = inTXTLen;
22423 }
22424
22425 instance->port = ntohs( inPort );
22426
22427 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
22428 {
22429 DNSServiceRef sdRef;
22430
22431 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
22432
22433 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
22434 require_noerr( err, exit );
22435
22436 DNSServiceForget( &instance->getAddrInfo );
22437 ForgetSBIPAddressList( &instance->ipaddrList );
22438
22439 sb_ulog( kLogLevelTrace, "Starting GetAddrInfo on interface %d for %s",
22440 (int32_t) instance->ifIndex, instance->hostname );
22441
22442 sdRef = me->connection;
22443 instance->gaiStartTicks = UpTicks();
22444 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
22445 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback, instance );
22446 require_noerr( err, exit );
22447
22448 instance->getAddrInfo = sdRef;
22449 }
22450
22451 exit:
22452 return;
22453 }
22454
22455 //===========================================================================================================================
22456 // _ServiceBrowserGAICallback
22457 //===========================================================================================================================
22458
22459 static void DNSSD_API
22460 _ServiceBrowserGAICallback(
22461 DNSServiceRef inSDRef,
22462 DNSServiceFlags inFlags,
22463 uint32_t inInterfaceIndex,
22464 DNSServiceErrorType inError,
22465 const char * inHostname,
22466 const struct sockaddr * inSockAddr,
22467 uint32_t inTTL,
22468 void * inContext )
22469 {
22470 OSStatus err;
22471 const uint64_t nowTicks = UpTicks();
22472 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22473 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22474
22475 Unused( inSDRef );
22476 Unused( inTTL );
22477
22478 sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22479 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
22480 !inError, inError );
22481
22482 require_noerr( inError, exit );
22483
22484 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22485 {
22486 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22487 goto exit;
22488 }
22489
22490 if( inFlags & kDNSServiceFlagsAdd )
22491 {
22492 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
22493 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
22494 if( err == kDuplicateErr ) err = kNoErr;
22495 require_noerr( err, exit );
22496 }
22497 else
22498 {
22499 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
22500 if( err == kNotFoundErr ) err = kNoErr;
22501 require_noerr( err, exit );
22502 }
22503
22504 exit:
22505 return;
22506 }
22507
22508 //===========================================================================================================================
22509 // _ServiceBrowserAddServiceType
22510 //===========================================================================================================================
22511
22512 static OSStatus
22513 _ServiceBrowserAddServiceType(
22514 ServiceBrowserRef me,
22515 SBDomain * inDomain,
22516 const char * inName,
22517 uint32_t inIfIndex )
22518 {
22519 OSStatus err;
22520 SBServiceType * type;
22521 SBServiceType ** typePtr;
22522 SBServiceType * newType = NULL;
22523 SBServiceBrowse * browse;
22524 SBServiceBrowse ** browsePtr;
22525 SBServiceBrowse * newBrowse = NULL;
22526 DNSServiceRef sdRef;
22527 DNSServiceFlags flags;
22528
22529 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22530 {
22531 if( strcasecmp( type->name, inName ) == 0 ) break;
22532 }
22533 if( !type )
22534 {
22535 err = _SBServiceTypeCreate( inName, &newType );
22536 require_noerr_quiet( err, exit );
22537
22538 type = newType;
22539 }
22540
22541 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22542 {
22543 if( browse->ifIndex == inIfIndex ) break;
22544 }
22545 require_action_quiet( !browse, exit, err = kDuplicateErr );
22546
22547 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
22548 require_noerr_quiet( err, exit );
22549
22550 flags = kDNSServiceFlagsShareConnection;
22551 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22552
22553 sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
22554 (int32_t) newBrowse->ifIndex, type->name, inDomain->name );
22555
22556 sdRef = me->connection;
22557 newBrowse->startTicks = UpTicks();
22558 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
22559 newBrowse );
22560 require_noerr( err, exit );
22561
22562 newBrowse->browse = sdRef;
22563 *browsePtr = newBrowse;
22564 newBrowse = NULL;
22565
22566 if( newType )
22567 {
22568 *typePtr = newType;
22569 newType = NULL;
22570 }
22571
22572 exit:
22573 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
22574 if( newType ) _SBServiceTypeFree( newType );
22575 return( err );
22576 }
22577
22578 //===========================================================================================================================
22579 // _ServiceBrowserRemoveServiceType
22580 //===========================================================================================================================
22581
22582 static OSStatus
22583 _ServiceBrowserRemoveServiceType(
22584 ServiceBrowserRef me,
22585 SBDomain * inDomain,
22586 const char * inName,
22587 uint32_t inIfIndex )
22588 {
22589 OSStatus err;
22590 SBServiceType * type;
22591 SBServiceType ** typePtr;
22592 SBServiceBrowse * browse;
22593 SBServiceBrowse ** browsePtr;
22594
22595 Unused( me );
22596
22597 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22598 {
22599 if( strcasecmp( type->name, inName ) == 0 ) break;
22600 }
22601 require_action_quiet( type, exit, err = kNotFoundErr );
22602
22603 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22604 {
22605 if( browse->ifIndex == inIfIndex ) break;
22606 }
22607 require_action_quiet( browse, exit, err = kNotFoundErr );
22608
22609 *browsePtr = browse->next;
22610 _SBServiceBrowseFree( browse );
22611 if( !type->browseList )
22612 {
22613 *typePtr = type->next;
22614 _SBServiceTypeFree( type );
22615 }
22616 err = kNoErr;
22617
22618 exit:
22619 return( err );
22620 }
22621
22622 //===========================================================================================================================
22623 // _ServiceBrowserAddServiceInstance
22624 //===========================================================================================================================
22625
22626 static OSStatus
22627 _ServiceBrowserAddServiceInstance(
22628 ServiceBrowserRef me,
22629 SBServiceBrowse * inBrowse,
22630 uint32_t inIfIndex,
22631 const char * inName,
22632 const char * inRegType,
22633 const char * inDomain,
22634 uint64_t inDiscoverTimeUs )
22635 {
22636 OSStatus err;
22637 DNSServiceRef sdRef;
22638 SBServiceInstance * instance;
22639 SBServiceInstance ** instancePtr;
22640 SBServiceInstance * newInstance = NULL;
22641
22642 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
22643 {
22644 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22645 }
22646 require_action_quiet( !instance, exit, err = kDuplicateErr );
22647
22648 err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
22649 require_noerr_quiet( err, exit );
22650
22651 sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
22652 (int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
22653
22654 sdRef = me->connection;
22655 newInstance->resolveStartTicks = UpTicks();
22656 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
22657 _ServiceBrowserResolveCallback, newInstance );
22658 require_noerr( err, exit );
22659
22660 newInstance->resolve = sdRef;
22661 *instancePtr = newInstance;
22662 newInstance = NULL;
22663
22664 exit:
22665 if( newInstance ) _SBServiceInstanceFree( newInstance );
22666 return( err );
22667 }
22668
22669 //===========================================================================================================================
22670 // _ServiceBrowserRemoveServiceInstance
22671 //===========================================================================================================================
22672
22673 static OSStatus
22674 _ServiceBrowserRemoveServiceInstance(
22675 ServiceBrowserRef me,
22676 SBServiceBrowse * inBrowse,
22677 const char * inName,
22678 uint32_t inIfIndex )
22679 {
22680 OSStatus err;
22681 SBServiceInstance * instance;
22682 SBServiceInstance ** ptr;
22683
22684 Unused( me );
22685
22686 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
22687 {
22688 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22689 }
22690 require_action_quiet( instance, exit, err = kNotFoundErr );
22691
22692 *ptr = instance->next;
22693 _SBServiceInstanceFree( instance );
22694 err = kNoErr;
22695
22696 exit:
22697 return( err );
22698 }
22699
22700 //===========================================================================================================================
22701 // _ServiceBrowserAddIPAddress
22702 //===========================================================================================================================
22703
22704 static OSStatus
22705 _ServiceBrowserAddIPAddress(
22706 ServiceBrowserRef me,
22707 SBServiceInstance * inInstance,
22708 const struct sockaddr * inSockAddr,
22709 uint64_t inResolveTimeUs )
22710 {
22711 OSStatus err;
22712 SBIPAddress * ipaddr;
22713 SBIPAddress ** ipaddrPtr;
22714 SBIPAddress * newIPAddr = NULL;
22715
22716 Unused( me );
22717
22718 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22719 {
22720 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22721 err = kTypeErr;
22722 goto exit;
22723 }
22724
22725 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22726 {
22727 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
22728 }
22729 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
22730
22731 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
22732 require_noerr_quiet( err, exit );
22733
22734 *ipaddrPtr = newIPAddr;
22735 newIPAddr = NULL;
22736 err = kNoErr;
22737
22738 exit:
22739 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
22740 return( err );
22741 }
22742
22743 //===========================================================================================================================
22744 // _ServiceBrowserRemoveIPAddress
22745 //===========================================================================================================================
22746
22747 static OSStatus
22748 _ServiceBrowserRemoveIPAddress(
22749 ServiceBrowserRef me,
22750 SBServiceInstance * inInstance,
22751 const struct sockaddr * inSockAddr )
22752 {
22753 OSStatus err;
22754 SBIPAddress * ipaddr;
22755 SBIPAddress ** ipaddrPtr;
22756
22757 Unused( me );
22758
22759 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22760 {
22761 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
22762 }
22763 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
22764
22765 *ipaddrPtr = ipaddr->next;
22766 _SBIPAddressFree( ipaddr );
22767 err = kNoErr;
22768
22769 exit:
22770 return( err );
22771 }
22772
22773 //===========================================================================================================================
22774 // _ServiceBrowserCreateResults
22775 //===========================================================================================================================
22776
22777 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
22778 {
22779 OSStatus err;
22780 SBDomain * d;
22781 SBServiceType * t;
22782 SBServiceBrowse * b;
22783 SBServiceInstance * i;
22784 SBIPAddress * a;
22785 ServiceBrowserResultsPrivate * results;
22786 SBRDomain ** domainPtr;
22787
22788 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
22789 require_action( results, exit, err = kNoMemoryErr );
22790
22791 results->refCount = 1;
22792
22793 domainPtr = &results->domainList;
22794 for( d = me->domainList; d; d = d->next )
22795 {
22796 SBRDomain * domain;
22797 SBRServiceType ** typePtr;
22798
22799 err = _SBRDomainCreate( d->name, &domain );
22800 require_noerr_quiet( err, exit );
22801 *domainPtr = domain;
22802 domainPtr = &domain->next;
22803
22804 typePtr = &domain->typeList;
22805 for( t = d->typeList; t; t = t->next )
22806 {
22807 SBRServiceType * type;
22808 SBRServiceInstance ** instancePtr;
22809
22810 err = _SBRServiceTypeCreate( t->name, &type );
22811 require_noerr_quiet( err, exit );
22812 *typePtr = type;
22813 typePtr = &type->next;
22814
22815 instancePtr = &type->instanceList;
22816 for( b = t->browseList; b; b = b->next )
22817 {
22818 for( i = b->instanceList; i; i = i->next )
22819 {
22820 SBRServiceInstance * instance;
22821 SBRIPAddress ** ipaddrPtr;
22822
22823 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
22824 i->discoverTimeUs, i->resolveTimeUs, &instance );
22825 require_noerr_quiet( err, exit );
22826 *instancePtr = instance;
22827 instancePtr = &instance->next;
22828
22829 ipaddrPtr = &instance->ipaddrList;
22830 for( a = i->ipaddrList; a; a = a->next )
22831 {
22832 SBRIPAddress * ipaddr;
22833
22834 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
22835 require_noerr_quiet( err, exit );
22836
22837 *ipaddrPtr = ipaddr;
22838 ipaddrPtr = &ipaddr->next;
22839 }
22840 }
22841 }
22842 }
22843 }
22844
22845 *outResults = (ServiceBrowserResults *) results;
22846 results = NULL;
22847 err = kNoErr;
22848
22849 exit:
22850 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
22851 return( err );
22852 }
22853
22854 //===========================================================================================================================
22855 // _SBDomainCreate
22856 //===========================================================================================================================
22857
22858 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
22859 {
22860 OSStatus err;
22861 SBDomain * obj;
22862
22863 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
22864 require_action( obj, exit, err = kNoMemoryErr );
22865
22866 obj->name = strdup( inName );
22867 require_action( obj->name, exit, err = kNoMemoryErr );
22868
22869 obj->browser = inBrowser;
22870
22871 *outDomain = obj;
22872 obj = NULL;
22873 err = kNoErr;
22874
22875 exit:
22876 if( obj ) _SBDomainFree( obj );
22877 return( err );
22878 }
22879
22880 //===========================================================================================================================
22881 // _SBDomainFree
22882 //===========================================================================================================================
22883
22884 static void _SBDomainFree( SBDomain *inDomain )
22885 {
22886 SBServiceType * type;
22887
22888 ForgetMem( &inDomain->name );
22889 DNSServiceForget( &inDomain->servicesQuery );
22890 while( ( type = inDomain->typeList ) != NULL )
22891 {
22892 inDomain->typeList = type->next;
22893 _SBServiceTypeFree( type );
22894 }
22895 free( inDomain );
22896 }
22897
22898 //===========================================================================================================================
22899 // _SBServiceTypeCreate
22900 //===========================================================================================================================
22901
22902 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
22903 {
22904 OSStatus err;
22905 SBServiceType * obj;
22906
22907 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
22908 require_action( obj, exit, err = kNoMemoryErr );
22909
22910 obj->name = strdup( inName );
22911 require_action( obj->name, exit, err = kNoMemoryErr );
22912
22913 *outType = obj;
22914 obj = NULL;
22915 err = kNoErr;
22916
22917 exit:
22918 if( obj ) _SBServiceTypeFree( obj );
22919 return( err );
22920 }
22921
22922 //===========================================================================================================================
22923 // _SBServiceTypeFree
22924 //===========================================================================================================================
22925
22926 static void _SBServiceTypeFree( SBServiceType *inType )
22927 {
22928 SBServiceBrowse * browse;
22929
22930 ForgetMem( &inType->name );
22931 while( ( browse = inType->browseList ) != NULL )
22932 {
22933 inType->browseList = browse->next;
22934 _SBServiceBrowseFree( browse );
22935 }
22936 free( inType );
22937 }
22938
22939 //===========================================================================================================================
22940 // _SBServiceBrowseCreate
22941 //===========================================================================================================================
22942
22943 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
22944 {
22945 OSStatus err;
22946 SBServiceBrowse * obj;
22947
22948 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
22949 require_action( obj, exit, err = kNoMemoryErr );
22950
22951 obj->ifIndex = inIfIndex;
22952 obj->browser = inBrowser;
22953 *outBrowse = obj;
22954 err = kNoErr;
22955
22956 exit:
22957 return( err );
22958 }
22959
22960 //===========================================================================================================================
22961 // _SBServiceBrowseFree
22962 //===========================================================================================================================
22963
22964 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
22965 {
22966 SBServiceInstance * instance;
22967
22968 DNSServiceForget( &inBrowse->browse );
22969 while( ( instance = inBrowse->instanceList ) != NULL )
22970 {
22971 inBrowse->instanceList = instance->next;
22972 _SBServiceInstanceFree( instance );
22973 }
22974 free( inBrowse );
22975 }
22976
22977 //===========================================================================================================================
22978 // _SBServiceInstanceCreate
22979 //===========================================================================================================================
22980
22981 static OSStatus
22982 _SBServiceInstanceCreate(
22983 const char * inName,
22984 const char * inType,
22985 const char * inDomain,
22986 uint32_t inIfIndex,
22987 uint64_t inDiscoverTimeUs,
22988 ServiceBrowserRef inBrowser,
22989 SBServiceInstance ** outInstance )
22990 {
22991 OSStatus err;
22992 SBServiceInstance * obj;
22993
22994 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
22995 require_action( obj, exit, err = kNoMemoryErr );
22996
22997 obj->name = strdup( inName );
22998 require_action( obj->name, exit, err = kNoMemoryErr );
22999
23000 ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
23001 require_action( obj->fqdn, exit, err = kNoMemoryErr );
23002
23003 obj->ifIndex = inIfIndex;
23004 obj->discoverTimeUs = inDiscoverTimeUs;
23005 obj->browser = inBrowser;
23006
23007 *outInstance = obj;
23008 obj = NULL;
23009 err = kNoErr;
23010
23011 exit:
23012 if( obj ) _SBServiceInstanceFree( obj );
23013 return( err );
23014 }
23015
23016 //===========================================================================================================================
23017 // _SBServiceInstanceFree
23018 //===========================================================================================================================
23019
23020 static void _SBServiceInstanceFree( SBServiceInstance *inInstance )
23021 {
23022 ForgetMem( &inInstance->name );
23023 ForgetMem( &inInstance->fqdn );
23024 DNSServiceForget( &inInstance->resolve );
23025 ForgetMem( &inInstance->hostname );
23026 ForgetMem( &inInstance->txtPtr );
23027 DNSServiceForget( &inInstance->getAddrInfo );
23028 ForgetSBIPAddressList( &inInstance->ipaddrList );
23029 free( inInstance );
23030 }
23031
23032 //===========================================================================================================================
23033 // _SBIPAddressCreate
23034 //===========================================================================================================================
23035
23036 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
23037 {
23038 OSStatus err;
23039 SBIPAddress * obj;
23040
23041 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
23042 require_action( obj, exit, err = kNoMemoryErr );
23043
23044 SockAddrCopy( inSockAddr, &obj->sip );
23045 obj->resolveTimeUs = inResolveTimeUs;
23046
23047 *outIPAddress = obj;
23048 err = kNoErr;
23049
23050 exit:
23051 return( err );
23052 }
23053
23054 //===========================================================================================================================
23055 // _SBIPAddressFree
23056 //===========================================================================================================================
23057
23058 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
23059 {
23060 free( inIPAddress );
23061 }
23062
23063 //===========================================================================================================================
23064 // _SBIPAddressFreeList
23065 //===========================================================================================================================
23066
23067 static void _SBIPAddressFreeList( SBIPAddress *inList )
23068 {
23069 SBIPAddress * ipaddr;
23070
23071 while( ( ipaddr = inList ) != NULL )
23072 {
23073 inList = ipaddr->next;
23074 _SBIPAddressFree( ipaddr );
23075 }
23076 }
23077
23078 //===========================================================================================================================
23079 // _SBRDomainCreate
23080 //===========================================================================================================================
23081
23082 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
23083 {
23084 OSStatus err;
23085 SBRDomain * obj;
23086
23087 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
23088 require_action( obj, exit, err = kNoMemoryErr );
23089
23090 obj->name = strdup( inName );
23091 require_action( obj->name, exit, err = kNoMemoryErr );
23092
23093 *outDomain = obj;
23094 obj = NULL;
23095 err = kNoErr;
23096
23097 exit:
23098 if( obj ) _SBRDomainFree( obj );
23099 return( err );
23100 }
23101
23102 //===========================================================================================================================
23103 // _SBRDomainFree
23104 //===========================================================================================================================
23105
23106 static void _SBRDomainFree( SBRDomain *inDomain )
23107 {
23108 SBRServiceType * type;
23109
23110 ForgetMem( &inDomain->name );
23111 while( ( type = inDomain->typeList ) != NULL )
23112 {
23113 inDomain->typeList = type->next;
23114 _SBRServiceTypeFree( type );
23115 }
23116 free( inDomain );
23117 }
23118
23119 //===========================================================================================================================
23120 // _SBRServiceTypeCreate
23121 //===========================================================================================================================
23122
23123 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
23124 {
23125 OSStatus err;
23126 SBRServiceType * obj;
23127
23128 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
23129 require_action( obj, exit, err = kNoMemoryErr );
23130
23131 obj->name = strdup( inName );
23132 require_action( obj->name, exit, err = kNoMemoryErr );
23133
23134 *outType = obj;
23135 obj = NULL;
23136 err = kNoErr;
23137
23138 exit:
23139 if( obj ) _SBRServiceTypeFree( obj );
23140 return( err );
23141 }
23142
23143 //===========================================================================================================================
23144 // _SBRServiceTypeFree
23145 //===========================================================================================================================
23146
23147 static void _SBRServiceTypeFree( SBRServiceType *inType )
23148 {
23149 SBRServiceInstance * instance;
23150
23151 ForgetMem( &inType->name );
23152 while( ( instance = inType->instanceList ) != NULL )
23153 {
23154 inType->instanceList = instance->next;
23155 _SBRServiceInstanceFree( instance );
23156 }
23157 free( inType );
23158 }
23159
23160 //===========================================================================================================================
23161 // _SBRServiceInstanceCreate
23162 //===========================================================================================================================
23163
23164 static OSStatus
23165 _SBRServiceInstanceCreate(
23166 const char * inName,
23167 uint32_t inInterfaceIndex,
23168 const char * inHostname,
23169 uint16_t inPort,
23170 const uint8_t * inTXTPtr,
23171 size_t inTXTLen,
23172 uint64_t inDiscoverTimeUs,
23173 uint64_t inResolveTimeUs,
23174 SBRServiceInstance ** outInstance )
23175 {
23176 OSStatus err;
23177 SBRServiceInstance * obj;
23178
23179 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
23180 require_action( obj, exit, err = kNoMemoryErr );
23181
23182 obj->name = strdup( inName );
23183 require_action( obj->name, exit, err = kNoMemoryErr );
23184
23185 if( inHostname )
23186 {
23187 obj->hostname = strdup( inHostname );
23188 require_action( obj->hostname, exit, err = kNoMemoryErr );
23189 }
23190 if( inTXTLen > 0 )
23191 {
23192 obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
23193 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
23194 obj->txtLen = inTXTLen;
23195 }
23196 obj->discoverTimeUs = inDiscoverTimeUs;
23197 obj->resolveTimeUs = inResolveTimeUs;
23198 obj->ifIndex = inInterfaceIndex;
23199 obj->port = inPort;
23200
23201 *outInstance = obj;
23202 obj = NULL;
23203 err = kNoErr;
23204
23205 exit:
23206 if( obj ) _SBRServiceInstanceFree( obj );
23207 return( err );
23208 }
23209
23210 //===========================================================================================================================
23211 // _SBRServiceInstanceFree
23212 //===========================================================================================================================
23213
23214 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
23215 {
23216 SBRIPAddress * ipaddr;
23217
23218 ForgetMem( &inInstance->name );
23219 ForgetMem( &inInstance->hostname );
23220 ForgetMem( &inInstance->txtPtr );
23221 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
23222 {
23223 inInstance->ipaddrList = ipaddr->next;
23224 _SBRIPAddressFree( ipaddr );
23225 }
23226 free( inInstance );
23227 }
23228
23229 //===========================================================================================================================
23230 // _SBRIPAddressCreate
23231 //===========================================================================================================================
23232
23233 static OSStatus
23234 _SBRIPAddressCreate(
23235 const struct sockaddr * inSockAddr,
23236 uint64_t inResolveTimeUs,
23237 SBRIPAddress ** outIPAddress )
23238 {
23239 OSStatus err;
23240 SBRIPAddress * obj;
23241
23242 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
23243 require_action( obj, exit, err = kNoMemoryErr );
23244
23245 SockAddrCopy( inSockAddr, &obj->sip );
23246 obj->resolveTimeUs = inResolveTimeUs;
23247
23248 *outIPAddress = obj;
23249 err = kNoErr;
23250
23251 exit:
23252 return( err );
23253 }
23254
23255 //===========================================================================================================================
23256 // _SBRIPAddressFree
23257 //===========================================================================================================================
23258
23259 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
23260 {
23261 free( inIPAddress );
23262 }
23263
23264 //===========================================================================================================================
23265 // _SocketWriteAll
23266 //
23267 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23268 //===========================================================================================================================
23269
23270 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
23271 {
23272 OSStatus err;
23273 const uint8_t * src;
23274 const uint8_t * end;
23275 fd_set writeSet;
23276 struct timeval timeout;
23277 ssize_t n;
23278
23279 FD_ZERO( &writeSet );
23280 src = (const uint8_t *) inData;
23281 end = src + inSize;
23282 while( src < end )
23283 {
23284 FD_SET( inSock, &writeSet );
23285 timeout.tv_sec = inTimeoutSecs;
23286 timeout.tv_usec = 0;
23287 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
23288 if( n == 0 ) { err = kTimeoutErr; goto exit; }
23289 err = map_socket_value_errno( inSock, n > 0, n );
23290 require_noerr( err, exit );
23291
23292 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
23293 err = map_socket_value_errno( inSock, n >= 0, n );
23294 if( err == EINTR ) continue;
23295 require_noerr( err, exit );
23296
23297 src += n;
23298 }
23299 err = kNoErr;
23300
23301 exit:
23302 return( err );
23303 }
23304
23305 //===========================================================================================================================
23306 // _ParseIPv4Address
23307 //
23308 // Warning: "inBuffer" may be modified even in error cases.
23309 //
23310 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23311 //===========================================================================================================================
23312
23313 static OSStatus _ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
23314 {
23315 OSStatus err;
23316 uint8_t * dst;
23317 int segments;
23318 int sawDigit;
23319 int c;
23320 int v;
23321
23322 check( inBuffer );
23323 check( outStr );
23324
23325 dst = inBuffer;
23326 *dst = 0;
23327 sawDigit = 0;
23328 segments = 0;
23329 for( ; ( c = *inStr ) != '\0'; ++inStr )
23330 {
23331 if( isdigit_safe( c ) )
23332 {
23333 v = ( *dst * 10 ) + ( c - '0' );
23334 require_action_quiet( v <= 255, exit, err = kRangeErr );
23335 *dst = (uint8_t) v;
23336 if( !sawDigit )
23337 {
23338 ++segments;
23339 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
23340 sawDigit = 1;
23341 }
23342 }
23343 else if( ( c == '.' ) && sawDigit )
23344 {
23345 require_action_quiet( segments < 4, exit, err = kMalformedErr );
23346 *++dst = 0;
23347 sawDigit = 0;
23348 }
23349 else
23350 {
23351 break;
23352 }
23353 }
23354 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
23355
23356 *outStr = inStr;
23357 err = kNoErr;
23358
23359 exit:
23360 return( err );
23361 }
23362
23363 //===========================================================================================================================
23364 // _StringToIPv4Address
23365 //
23366 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23367 //===========================================================================================================================
23368
23369 static OSStatus
23370 _StringToIPv4Address(
23371 const char * inStr,
23372 StringToIPAddressFlags inFlags,
23373 uint32_t * outIP,
23374 int * outPort,
23375 uint32_t * outSubnet,
23376 uint32_t * outRouter,
23377 const char ** outStr )
23378 {
23379 OSStatus err;
23380 uint8_t buf[ 4 ];
23381 int c;
23382 uint32_t ip;
23383 int hasPort;
23384 int port;
23385 int hasPrefix;
23386 int prefix;
23387 uint32_t subnetMask;
23388 uint32_t router;
23389
23390 require_action( inStr, exit, err = kParamErr );
23391
23392 // Parse the address-only part of the address (e.g. "1.2.3.4").
23393
23394 err = _ParseIPv4Address( inStr, buf, &inStr );
23395 require_noerr_quiet( err, exit );
23396 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
23397 c = *inStr;
23398
23399 // Parse the port (if any).
23400
23401 hasPort = 0;
23402 port = 0;
23403 if( c == ':' )
23404 {
23405 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23406 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23407 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23408 hasPort = 1;
23409 }
23410
23411 // Parse the prefix length (if any).
23412
23413 hasPrefix = 0;
23414 prefix = 0;
23415 subnetMask = 0;
23416 router = 0;
23417 if( c == '/' )
23418 {
23419 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23420 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23421 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
23422 hasPrefix = 1;
23423
23424 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
23425 router = ( ip & subnetMask ) | 1;
23426 }
23427
23428 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23429
23430 if( outIP ) *outIP = ip;
23431 if( outPort && hasPort ) *outPort = port;
23432 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
23433 if( outRouter && hasPrefix ) *outRouter = router;
23434 if( outStr ) *outStr = inStr;
23435 err = kNoErr;
23436
23437 exit:
23438 return( err );
23439 }
23440
23441 //===========================================================================================================================
23442 // _ParseIPv6Address
23443 //
23444 // Note: Parsed according to the rules specified in RFC 3513.
23445 // Warning: "inBuffer" may be modified even in error cases.
23446 //
23447 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23448 //===========================================================================================================================
23449
23450 static OSStatus _ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
23451 {
23452 // Table to map uppercase hex characters - '0' to their numeric values.
23453 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23454 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 };
23455 OSStatus err;
23456 const char * ptr;
23457 uint8_t * dst;
23458 uint8_t * lim;
23459 uint8_t * colonPtr;
23460 int c;
23461 int sawDigit;
23462 unsigned int v;
23463 int i;
23464 int n;
23465
23466 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23467
23468 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
23469
23470 // Special case leading :: (e.g. "::1") to simplify processing later.
23471
23472 if( *inStr == ':' )
23473 {
23474 ++inStr;
23475 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
23476 }
23477
23478 // Parse the address.
23479
23480 ptr = inStr;
23481 dst = inBuffer;
23482 lim = dst + 16;
23483 colonPtr = NULL;
23484 sawDigit = 0;
23485 v = 0;
23486 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
23487 {
23488 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
23489 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
23490 {
23491 c -= '0';
23492 check( c < (int) countof( kASCIItoHexTable ) );
23493 v = ( v << 4 ) | kASCIItoHexTable[ c ];
23494 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
23495 sawDigit = 1;
23496 continue;
23497 }
23498 if( c == ':' )
23499 {
23500 ptr = inStr;
23501 if( !sawDigit )
23502 {
23503 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
23504 colonPtr = dst;
23505 continue;
23506 }
23507 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
23508 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23509 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23510 *dst++ = (uint8_t)( v & 0xFF );
23511 sawDigit = 0;
23512 v = 0;
23513 continue;
23514 }
23515
23516 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23517
23518 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
23519 {
23520 err = _ParseIPv4Address( ptr, dst, &inStr );
23521 require_noerr_quiet( err, exit );
23522 dst += 4;
23523 sawDigit = 0;
23524 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
23525 }
23526 break;
23527 }
23528 if( sawDigit )
23529 {
23530 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23531 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23532 *dst++ = (uint8_t)( v & 0xFF );
23533 }
23534 check( dst <= lim );
23535 if( colonPtr )
23536 {
23537 require_action_quiet( dst < lim, exit, err = kOverrunErr );
23538 n = (int)( dst - colonPtr );
23539 for( i = 1; i <= n; ++i )
23540 {
23541 lim[ -i ] = colonPtr[ n - i ];
23542 colonPtr[ n - i ] = 0;
23543 }
23544 dst = lim;
23545 }
23546 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
23547
23548 *outStr = inStr - 1;
23549 err = kNoErr;
23550
23551 exit:
23552 return( err );
23553 }
23554
23555 //===========================================================================================================================
23556 // _ParseIPv6Scope
23557 //
23558 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23559 //===========================================================================================================================
23560
23561 static OSStatus _ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
23562 {
23563 #if( TARGET_OS_POSIX )
23564 OSStatus err;
23565 char scopeStr[ 64 ];
23566 char * dst;
23567 char * lim;
23568 int c;
23569 uint32_t scope;
23570 const char * ptr;
23571
23572 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23573
23574 dst = scopeStr;
23575 lim = dst + ( countof( scopeStr ) - 1 );
23576 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
23577 {
23578 *dst++ = *inStr++;
23579 }
23580 *dst = '\0';
23581 check( dst <= lim );
23582
23583 // First try to map as a name and if that fails, treat it as a numeric scope.
23584
23585 scope = if_nametoindex( scopeStr );
23586 if( scope == 0 )
23587 {
23588 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
23589 {
23590 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
23591 }
23592 require_action_quiet( c == '\0', exit, err = kMalformedErr );
23593 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
23594 }
23595
23596 *outScope = scope;
23597 *outStr = inStr;
23598 err = kNoErr;
23599
23600 exit:
23601 return( err );
23602 #else
23603 OSStatus err;
23604 uint32_t scope;
23605 const char * start;
23606 int c;
23607
23608 scope = 0;
23609 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
23610 {
23611 scope = ( scope * 10 ) + ( c - '0' );
23612 }
23613 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
23614
23615 *outScope = scope;
23616 *outStr = inStr;
23617 err = kNoErr;
23618
23619 exit:
23620 return( err );
23621 #endif
23622 }
23623
23624 //===========================================================================================================================
23625 // _StringToIPv6Address
23626 //
23627 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23628 //===========================================================================================================================
23629
23630 static OSStatus
23631 _StringToIPv6Address(
23632 const char * inStr,
23633 StringToIPAddressFlags inFlags,
23634 uint8_t outIPv6[ 16 ],
23635 uint32_t * outScope,
23636 int * outPort,
23637 int * outPrefix,
23638 const char ** outStr )
23639 {
23640 OSStatus err;
23641 uint8_t ipv6[ 16 ];
23642 int c;
23643 int hasScope;
23644 uint32_t scope;
23645 int hasPort;
23646 int port;
23647 int hasPrefix;
23648 int prefix;
23649 int hasBracket;
23650 int i;
23651
23652 require_action( inStr, exit, err = kParamErr );
23653
23654 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23655
23656 // Parse the address-only part of the address (e.g. "1::1").
23657
23658 err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
23659 require_noerr_quiet( err, exit );
23660 c = *inStr;
23661
23662 // Parse the scope, port, or prefix length.
23663
23664 hasScope = 0;
23665 scope = 0;
23666 hasPort = 0;
23667 port = 0;
23668 hasPrefix = 0;
23669 prefix = 0;
23670 hasBracket = 0;
23671 for( ;; )
23672 {
23673 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
23674 {
23675 require_action_quiet( !hasScope, exit, err = kMalformedErr );
23676 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
23677 ++inStr;
23678 err = _ParseIPv6Scope( inStr, &scope, &inStr );
23679 require_noerr_quiet( err, exit );
23680 hasScope = 1;
23681 c = *inStr;
23682 }
23683 else if( c == ':' ) // Port (e.g. ":80")
23684 {
23685 require_action_quiet( !hasPort, exit, err = kMalformedErr );
23686 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23687 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23688 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23689 hasPort = 1;
23690 }
23691 else if( c == '/' ) // Prefix Length (e.g. "/64")
23692 {
23693 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
23694 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23695 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23696 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
23697 hasPrefix = 1;
23698 }
23699 else if( c == ']' )
23700 {
23701 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
23702 hasBracket = 1;
23703 c = *( ++inStr );
23704 }
23705 else
23706 {
23707 break;
23708 }
23709 }
23710
23711 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23712
23713 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
23714 if( outScope && hasScope ) *outScope = scope;
23715 if( outPort && hasPort ) *outPort = port;
23716 if( outPrefix && hasPrefix ) *outPrefix = prefix;
23717 if( outStr ) *outStr = inStr;
23718 err = kNoErr;
23719
23720 exit:
23721 return( err );
23722 }
23723
23724 //===========================================================================================================================
23725 // _StringArray_Free
23726 //
23727 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23728 //===========================================================================================================================
23729
23730 static void _StringArray_Free( char **inArray, size_t inCount )
23731 {
23732 size_t i;
23733
23734 for( i = 0; i < inCount; ++i )
23735 {
23736 free( inArray[ i ] );
23737 }
23738 if( inCount > 0 ) free( inArray );
23739 }
23740
23741 //===========================================================================================================================
23742 // _ParseQuotedEscapedString
23743 //
23744 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23745 //===========================================================================================================================
23746
23747 static Boolean
23748 _ParseQuotedEscapedString(
23749 const char * inSrc,
23750 const char * inEnd,
23751 const char * inDelimiters,
23752 char * inBuf,
23753 size_t inMaxLen,
23754 size_t * outCopiedLen,
23755 size_t * outTotalLen,
23756 const char ** outSrc )
23757 {
23758 const unsigned char * src;
23759 const unsigned char * end;
23760 unsigned char * dst;
23761 unsigned char * lim;
23762 unsigned char c;
23763 unsigned char c2;
23764 size_t totalLen;
23765 Boolean singleQuote;
23766 Boolean doubleQuote;
23767
23768 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
23769 src = (const unsigned char *) inSrc;
23770 end = (const unsigned char *) inEnd;
23771 dst = (unsigned char *) inBuf;
23772 lim = dst + inMaxLen;
23773 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
23774 if( src >= end ) return( false );
23775
23776 // Parse each argument from the string.
23777 //
23778 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23779
23780 totalLen = 0;
23781 singleQuote = false;
23782 doubleQuote = false;
23783 while( src < end )
23784 {
23785 c = *src++;
23786 if( singleQuote )
23787 {
23788 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23789
23790 if( c == '\'' )
23791 {
23792 singleQuote = false;
23793 continue;
23794 }
23795 }
23796 else if( doubleQuote )
23797 {
23798 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23799 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23800 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23801 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23802 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23803
23804 if( c == '"' )
23805 {
23806 doubleQuote = false;
23807 continue;
23808 }
23809 else if( c == '\\' )
23810 {
23811 if( src < end )
23812 {
23813 c2 = *src;
23814 if( ( c2 == '"' ) || ( c2 == '\\' ) )
23815 {
23816 ++src;
23817 c = c2;
23818 }
23819 else if( c2 == '\n' )
23820 {
23821 ++src;
23822 continue;
23823 }
23824 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
23825 {
23826 ++src;
23827 c = c2;
23828 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23829 {
23830 c = HexPairToByte( src );
23831 src += 2;
23832 }
23833 }
23834 else if( isoctal_safe( c2 ) )
23835 {
23836 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23837 {
23838 c = OctalTripleToByte( src );
23839 src += 3;
23840 }
23841 }
23842 }
23843 }
23844 }
23845 else if( strchr( inDelimiters, c ) )
23846 {
23847 break;
23848 }
23849 else if( c == '\\' )
23850 {
23851 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23852 // A backslash followed by a newline disappears completely.
23853 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23854 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23855
23856 if( src < end )
23857 {
23858 c = *src;
23859 if( c == '\n' )
23860 {
23861 ++src;
23862 continue;
23863 }
23864 else if( ( c == 'x' ) || ( c == 'X' ) )
23865 {
23866 ++src;
23867 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23868 {
23869 c = HexPairToByte( src );
23870 src += 2;
23871 }
23872 }
23873 else if( isoctal_safe( c ) )
23874 {
23875 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23876 {
23877 c = OctalTripleToByte( src );
23878 src += 3;
23879 }
23880 else
23881 {
23882 ++src;
23883 }
23884 }
23885 else
23886 {
23887 ++src;
23888 }
23889 }
23890 }
23891 else if( c == '\'' )
23892 {
23893 singleQuote = true;
23894 continue;
23895 }
23896 else if( c == '"' )
23897 {
23898 doubleQuote = true;
23899 continue;
23900 }
23901
23902 if( dst < lim )
23903 {
23904 if( inBuf ) *dst = c;
23905 ++dst;
23906 }
23907 ++totalLen;
23908 }
23909
23910 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
23911 if( outTotalLen ) *outTotalLen = totalLen;
23912 if( outSrc ) *outSrc = (const char *) src;
23913 return( true );
23914 }
23915
23916 //===========================================================================================================================
23917 // _ServerSocketOpenEx2
23918 //
23919 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23920 //===========================================================================================================================
23921
23922 static OSStatus
23923 _ServerSocketOpenEx2(
23924 int inFamily,
23925 int inType,
23926 int inProtocol,
23927 const void * inAddr,
23928 int inPort,
23929 int * outPort,
23930 int inRcvBufSize,
23931 Boolean inNoPortReuse,
23932 SocketRef * outSock )
23933 {
23934 OSStatus err;
23935 int port;
23936 SocketRef sock;
23937 int name;
23938 int option;
23939 sockaddr_ip sip;
23940 socklen_t len;
23941
23942 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
23943
23944 sock = socket( inFamily, inType, inProtocol );
23945 err = map_socket_creation_errno( sock );
23946 require_noerr_quiet( err, exit );
23947
23948 #if( defined( SO_NOSIGPIPE ) )
23949 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
23950 #endif
23951
23952 err = SocketMakeNonBlocking( sock );
23953 require_noerr( err, exit );
23954
23955 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23956 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23957 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23958 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23959
23960 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
23961 check_noerr( err );
23962
23963 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23964
23965 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
23966 {
23967 option = 1;
23968 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
23969 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
23970 err = map_socket_noerr_errno( sock, err );
23971 require_noerr( err, exit );
23972 }
23973
23974 if( inFamily == AF_INET )
23975 {
23976 // Bind to the port. If it fails, retry with a dynamic port.
23977
23978 memset( &sip.v4, 0, sizeof( sip.v4 ) );
23979 SIN_LEN_SET( &sip.v4 );
23980 sip.v4.sin_family = AF_INET;
23981 sip.v4.sin_port = htons( (uint16_t) port );
23982 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
23983 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
23984 err = map_socket_noerr_errno( sock, err );
23985 if( err && ( inPort < 0 ) )
23986 {
23987 sip.v4.sin_port = 0;
23988 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
23989 err = map_socket_noerr_errno( sock, err );
23990 }
23991 require_noerr( err, exit );
23992 }
23993 #if( defined( AF_INET6 ) )
23994 else if( inFamily == AF_INET6 )
23995 {
23996 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
23997
23998 option = 1;
23999 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
24000 err = map_socket_noerr_errno( sock, err );
24001 require_noerr( err, exit );
24002
24003 // Bind to the port. If it fails, retry with a dynamic port.
24004
24005 memset( &sip.v6, 0, sizeof( sip.v6 ) );
24006 SIN6_LEN_SET( &sip.v6 );
24007 sip.v6.sin6_family = AF_INET6;
24008 sip.v6.sin6_port = htons( (uint16_t) port );
24009 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
24010 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24011 err = map_socket_noerr_errno( sock, err );
24012 if( err && ( inPort < 0 ) )
24013 {
24014 sip.v6.sin6_port = 0;
24015 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24016 err = map_socket_noerr_errno( sock, err );
24017 }
24018 require_noerr( err, exit );
24019 }
24020 #endif
24021 else
24022 {
24023 dlogassert( "Unsupported family: %d", inFamily );
24024 err = kUnsupportedErr;
24025 goto exit;
24026 }
24027
24028 if( inType == SOCK_STREAM )
24029 {
24030 err = listen( sock, SOMAXCONN );
24031 err = map_socket_noerr_errno( sock, err );
24032 if( err )
24033 {
24034 err = listen( sock, 5 );
24035 err = map_socket_noerr_errno( sock, err );
24036 require_noerr( err, exit );
24037 }
24038 }
24039
24040 if( outPort )
24041 {
24042 len = (socklen_t) sizeof( sip );
24043 err = getsockname( sock, &sip.sa, &len );
24044 err = map_socket_noerr_errno( sock, err );
24045 require_noerr( err, exit );
24046
24047 *outPort = SockAddrGetPort( &sip );
24048 }
24049 *outSock = sock;
24050 sock = kInvalidSocketRef;
24051
24052 exit:
24053 ForgetSocket( &sock );
24054 return( err );
24055 }
24056
24057 //===========================================================================================================================
24058 // _memdup
24059 //
24060 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24061 //===========================================================================================================================
24062
24063 static void * _memdup( const void *inPtr, size_t inLen )
24064 {
24065 void * mem;
24066
24067 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24068 require( mem, exit );
24069 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
24070
24071 exit:
24072 return( mem );
24073 }
24074
24075 //===========================================================================================================================
24076 // _memicmp
24077 //
24078 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24079 //===========================================================================================================================
24080
24081 static int _memicmp( const void *inP1, const void *inP2, size_t inLen )
24082 {
24083 const unsigned char * p1;
24084 const unsigned char * e1;
24085 const unsigned char * p2;
24086 int c1;
24087 int c2;
24088
24089 p1 = (const unsigned char *) inP1;
24090 e1 = p1 + inLen;
24091 p2 = (const unsigned char *) inP2;
24092 while( p1 < e1 )
24093 {
24094 c1 = *p1++;
24095 c2 = *p2++;
24096 c1 = tolower( c1 );
24097 c2 = tolower( c2 );
24098 if( c1 < c2 ) return( -1 );
24099 if( c1 > c2 ) return( 1 );
24100 }
24101 return( 0 );
24102 }
24103
24104 //===========================================================================================================================
24105 // _FNV1
24106 //
24107 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24108 //===========================================================================================================================
24109
24110 static uint32_t _FNV1( const void *inData, size_t inSize )
24111 {
24112 const uint8_t * src = (const uint8_t *) inData;
24113 const uint8_t * const end = src + inSize;
24114 uint32_t hash;
24115
24116 hash = 0x811c9dc5U;
24117 while( src != end )
24118 {
24119 hash *= 0x01000193;
24120 hash ^= *src++;
24121 }
24122 return( hash );
24123 }