]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dnssdutil.c
mDNSResponder-1096.40.7.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
2006 #endif // MDNSRESPONDER_PROJECT
2007
2008 //===========================================================================================================================
2009 // Command Table
2010 //===========================================================================================================================
2011
2012 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2013
2014 static void BrowseCmd( void );
2015 static void GetAddrInfoCmd( void );
2016 static void QueryRecordCmd( void );
2017 static void RegisterCmd( void );
2018 static void RegisterRecordCmd( void );
2019 static void ResolveCmd( void );
2020 static void ReconfirmCmd( void );
2021 static void GetAddrInfoPOSIXCmd( void );
2022 static void ReverseLookupCmd( void );
2023 static void PortMappingCmd( void );
2024 static void BrowseAllCmd( void );
2025 static void GetAddrInfoStressCmd( void );
2026 static void DNSQueryCmd( void );
2027 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2028 static void DNSCryptCmd( void );
2029 #endif
2030 static void MDNSQueryCmd( void );
2031 static void PIDToUUIDCmd( void );
2032 static void DaemonVersionCmd( void );
2033
2034 static CLIOption kGlobalOpts[] =
2035 {
2036 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2037 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2038 CLI_OPTION_HELP(),
2039
2040 // Common commands.
2041
2042 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2043 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2044 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2045 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2046 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2047 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2048 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2049 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2050 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2051 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2052 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2053
2054 // Uncommon commands.
2055
2056 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2057 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2058 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2059 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2060 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2061 #endif
2062 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2063 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2064 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2065 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2066 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2067 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2068 Command( "ssdp", NULL, kSSDPOpts, "Simple Service Discovery Protocol (SSDP).", true ),
2069 #if( TARGET_OS_DARWIN )
2070 Command( "legacy", NULL, kLegacyOpts, "Legacy DNS API.", true ),
2071 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2072 Command( "xpcsend", XPCSendCmd, kXPCSendOpts, "Sends a message to an XPC service.", true ),
2073 #endif
2074 #if( MDNSRESPONDER_PROJECT )
2075 Command( "interfaceMonitor", InterfaceMonitorCmd, kInterfaceMonitorOpts, "mDNSResponder's interface monitor.", true ),
2076 Command( "dnsproxy", DNSProxyCmd, kDNSProxyOpts, "Enables mDNSResponder's DNS proxy.", true ),
2077 #endif
2078 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2079
2080 CLI_COMMAND_HELP(),
2081 CLI_OPTION_END()
2082 };
2083
2084 //===========================================================================================================================
2085 // Helper Prototypes
2086 //===========================================================================================================================
2087
2088 #define kExitReason_OneShotDone "one-shot done"
2089 #define kExitReason_ReceivedResponse "received response"
2090 #define kExitReason_SIGINT "interrupt signal"
2091 #define kExitReason_Timeout "timeout"
2092 #define kExitReason_TimeLimit "time limit"
2093
2094 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2095
2096 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2097
2098 typedef enum
2099 {
2100 kConnectionType_None = 0,
2101 kConnectionType_Normal = 1,
2102 kConnectionType_DelegatePID = 2,
2103 kConnectionType_DelegateUUID = 3
2104
2105 } ConnectionType;
2106
2107 typedef struct
2108 {
2109 ConnectionType type;
2110 union
2111 {
2112 int32_t pid;
2113 uint8_t uuid[ 16 ];
2114
2115 } delegate;
2116
2117 } ConnectionDesc;
2118
2119 static OSStatus
2120 CreateConnectionFromArgString(
2121 const char * inString,
2122 dispatch_queue_t inQueue,
2123 DNSServiceRef * outSDRef,
2124 ConnectionDesc * outDesc );
2125 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2126 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2127 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2128 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2129
2130 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2131
2132 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2133 static const char * RecordTypeToString( unsigned int inValue );
2134
2135 static OSStatus
2136 DNSRecordDataToString(
2137 const void * inRDataPtr,
2138 size_t inRDataLen,
2139 unsigned int inRDataType,
2140 const void * inMsgPtr,
2141 size_t inMsgLen,
2142 char ** outString );
2143
2144 static OSStatus
2145 DNSMessageToText(
2146 const uint8_t * inMsgPtr,
2147 size_t inMsgLen,
2148 Boolean inIsMDNS,
2149 Boolean inPrintRaw,
2150 char ** outText );
2151
2152 static OSStatus
2153 WriteDNSQueryMessage(
2154 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2155 uint16_t inMsgID,
2156 uint16_t inFlags,
2157 const char * inQName,
2158 uint16_t inQType,
2159 uint16_t inQClass,
2160 size_t * outMsgLen );
2161
2162 // Dispatch helpers
2163
2164 typedef void ( *DispatchHandler )( void *inContext );
2165
2166 static OSStatus
2167 DispatchSignalSourceCreate(
2168 int inSignal,
2169 DispatchHandler inEventHandler,
2170 void * inContext,
2171 dispatch_source_t * outSource );
2172 static OSStatus
2173 DispatchSocketSourceCreate(
2174 SocketRef inSock,
2175 dispatch_source_type_t inType,
2176 dispatch_queue_t inQueue,
2177 DispatchHandler inEventHandler,
2178 DispatchHandler inCancelHandler,
2179 void * inContext,
2180 dispatch_source_t * outSource );
2181
2182 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2183 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2184
2185 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2186 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2187
2188 static OSStatus
2189 DispatchTimerCreate(
2190 dispatch_time_t inStart,
2191 uint64_t inIntervalNs,
2192 uint64_t inLeewayNs,
2193 dispatch_queue_t inQueue,
2194 DispatchHandler inEventHandler,
2195 DispatchHandler inCancelHandler,
2196 void * inContext,
2197 dispatch_source_t * outTimer );
2198
2199 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2200 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2201
2202 static OSStatus
2203 DispatchProcessMonitorCreate(
2204 pid_t inPID,
2205 unsigned long inFlags,
2206 dispatch_queue_t inQueue,
2207 DispatchHandler inEventHandler,
2208 DispatchHandler inCancelHandler,
2209 void * inContext,
2210 dispatch_source_t * outMonitor );
2211
2212 static const char * ServiceTypeDescription( const char *inName );
2213
2214 typedef struct
2215 {
2216 SocketRef sock; // Socket.
2217 void * userContext; // User context.
2218 int32_t refCount; // Reference count.
2219
2220 } SocketContext;
2221
2222 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
2223 static SocketContext * SocketContextRetain( SocketContext *inContext );
2224 static void SocketContextRelease( SocketContext *inContext );
2225 static void SocketContextCancelHandler( void *inContext );
2226
2227 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2228
2229 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2230 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2231 #if( TARGET_OS_DARWIN )
2232 static int64_t _StringToInt64( const char *inString, OSStatus *outError );
2233 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError );
2234 static pid_t _StringToPID( const char *inString, OSStatus *outError );
2235 static OSStatus
2236 _ParseEscapedString(
2237 const char * inSrc,
2238 const char * inEnd,
2239 const char * inDelimiters,
2240 char * inBufPtr,
2241 size_t inBufLen,
2242 size_t * outCopiedLen,
2243 size_t * outActualLen,
2244 const char ** outPtr );
2245 #endif
2246 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2247 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2248 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2249 #if( TARGET_OS_DARWIN )
2250 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2251 #endif
2252 static OSStatus
2253 _ServerSocketOpenEx2(
2254 int inFamily,
2255 int inType,
2256 int inProtocol,
2257 const void * inAddr,
2258 int inPort,
2259 int * outPort,
2260 int inRcvBufSize,
2261 Boolean inNoPortReuse,
2262 SocketRef * outSock );
2263
2264 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2265 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2266
2267 static OSStatus
2268 CreateMulticastSocket(
2269 const struct sockaddr * inAddr,
2270 int inPort,
2271 const char * inIfName,
2272 uint32_t inIfIndex,
2273 Boolean inJoin,
2274 int * outPort,
2275 SocketRef * outSock );
2276
2277 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2278 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2279 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2280 static OSStatus CheckRootUser( void );
2281 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... );
2282 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2283 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2284 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2285 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2286 static OSStatus
2287 CreateNSECRecordData(
2288 const uint8_t * inNextDomainName,
2289 uint8_t ** outPtr,
2290 size_t * outLen,
2291 unsigned int inTypeCount,
2292 ... );
2293 static OSStatus
2294 AppendSOARecord(
2295 DataBuffer * inDB,
2296 const uint8_t * inNamePtr,
2297 size_t inNameLen,
2298 uint16_t inType,
2299 uint16_t inClass,
2300 uint32_t inTTL,
2301 const uint8_t * inMName,
2302 const uint8_t * inRName,
2303 uint32_t inSerial,
2304 uint32_t inRefresh,
2305 uint32_t inRetry,
2306 uint32_t inExpire,
2307 uint32_t inMinimumTTL,
2308 size_t * outLen );
2309 static OSStatus
2310 CreateSOARecordData(
2311 const uint8_t * inMName,
2312 const uint8_t * inRName,
2313 uint32_t inSerial,
2314 uint32_t inRefresh,
2315 uint32_t inRetry,
2316 uint32_t inExpire,
2317 uint32_t inMinimumTTL,
2318 uint8_t ** outPtr,
2319 size_t * outLen );
2320 static OSStatus
2321 _DataBuffer_AppendDNSQuestion(
2322 DataBuffer * inDB,
2323 const uint8_t * inNamePtr,
2324 size_t inNameLen,
2325 uint16_t inType,
2326 uint16_t inClass );
2327 static OSStatus
2328 _DataBuffer_AppendDNSRecord(
2329 DataBuffer * inDB,
2330 const uint8_t * inNamePtr,
2331 size_t inNameLen,
2332 uint16_t inType,
2333 uint16_t inClass,
2334 uint32_t inTTL,
2335 const uint8_t * inRDataPtr,
2336 size_t inRDataLen );
2337 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2338
2339 typedef struct MDNSInterfaceItem MDNSInterfaceItem;
2340 struct MDNSInterfaceItem
2341 {
2342 MDNSInterfaceItem * next;
2343 char * ifName;
2344 uint32_t ifIndex;
2345 Boolean hasIPv4;
2346 Boolean hasIPv6;
2347 Boolean isAWDL;
2348 Boolean isWiFi;
2349 };
2350
2351 typedef enum
2352 {
2353 kMDNSInterfaceSubset_All = 0, // All mDNS-capable interfaces.
2354 kMDNSInterfaceSubset_AWDL = 1, // All mDNS-capable AWDL interfaces.
2355 kMDNSInterfaceSubset_NonAWDL = 2 // All mDNS-capable non-AWDL iterfaces.
2356
2357 } MDNSInterfaceSubset;
2358
2359 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2360 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2361 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2362 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2363
2364 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2365 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName );
2366 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName );
2367 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2368
2369 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2370 static OSStatus
2371 _StringToIPv4Address(
2372 const char * inStr,
2373 StringToIPAddressFlags inFlags,
2374 uint32_t * outIP,
2375 int * outPort,
2376 uint32_t * outSubnet,
2377 uint32_t * outRouter,
2378 const char ** outStr );
2379 static void _StringArray_Free( char **inArray, size_t inCount );
2380 static OSStatus
2381 _StringToIPv6Address(
2382 const char * inStr,
2383 StringToIPAddressFlags inFlags,
2384 uint8_t outIPv6[ 16 ],
2385 uint32_t * outScope,
2386 int * outPort,
2387 int * outPrefix,
2388 const char ** outStr );
2389 static Boolean
2390 _ParseQuotedEscapedString(
2391 const char * inSrc,
2392 const char * inEnd,
2393 const char * inDelimiters,
2394 char * inBuf,
2395 size_t inMaxLen,
2396 size_t * outCopiedLen,
2397 size_t * outTotalLen,
2398 const char ** outSrc );
2399 static void * _memdup( const void *inPtr, size_t inLen );
2400 static int _memicmp( const void *inP1, const void *inP2, size_t inLen );
2401 static uint32_t _FNV1( const void *inData, size_t inSize );
2402
2403 #define Unused( X ) (void)(X)
2404
2405 //===========================================================================================================================
2406 // MDNSCollider
2407 //===========================================================================================================================
2408
2409 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2410
2411 typedef uint32_t MDNSColliderProtocols;
2412 #define kMDNSColliderProtocol_None 0
2413 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2414 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2415
2416 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2417
2418 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2419 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2420 static void MDNSColliderStop( MDNSColliderRef inCollider );
2421 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2422 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2423 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2424 static void
2425 MDNSColliderSetStopHandler(
2426 MDNSColliderRef inCollider,
2427 MDNSColliderStopHandler_f inStopHandler,
2428 void * inStopContext );
2429 static OSStatus
2430 MDNSColliderSetRecord(
2431 MDNSColliderRef inCollider,
2432 const uint8_t * inName,
2433 uint16_t inType,
2434 const void * inRDataPtr,
2435 size_t inRDataLen );
2436 static CFTypeID MDNSColliderGetTypeID( void );
2437
2438 //===========================================================================================================================
2439 // ServiceBrowser
2440 //===========================================================================================================================
2441
2442 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2443 typedef struct ServiceBrowserResults ServiceBrowserResults;
2444 typedef struct SBRDomain SBRDomain;
2445 typedef struct SBRServiceType SBRServiceType;
2446 typedef struct SBRServiceInstance SBRServiceInstance;
2447 typedef struct SBRIPAddress SBRIPAddress;
2448
2449 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2450
2451 struct ServiceBrowserResults
2452 {
2453 SBRDomain * domainList; // List of domains in which services were found.
2454 };
2455
2456 struct SBRDomain
2457 {
2458 SBRDomain * next; // Next domain in list.
2459 char * name; // Name of domain represented by this object.
2460 SBRServiceType * typeList; // List of service types in this domain.
2461 };
2462
2463 struct SBRServiceType
2464 {
2465 SBRServiceType * next; // Next service type in list.
2466 char * name; // Name of service type represented by this object.
2467 SBRServiceInstance * instanceList; // List of service instances of this service type.
2468 };
2469
2470 struct SBRServiceInstance
2471 {
2472 SBRServiceInstance * next; // Next service instance in list.
2473 char * name; // Name of service instance represented by this object.
2474 char * hostname; // Target from service instance's SRV record.
2475 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2476 uint16_t port; // Port from service instance's SRV record.
2477 uint8_t * txtPtr; // Service instance's TXT record data.
2478 size_t txtLen; // Service instance's TXT record data length.
2479 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2480 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2481 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2482 };
2483
2484 struct SBRIPAddress
2485 {
2486 SBRIPAddress * next; // Next IP address in list.
2487 sockaddr_ip sip; // IPv4 or IPv6 address.
2488 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2489 };
2490
2491 static CFTypeID ServiceBrowserGetTypeID( void );
2492 static OSStatus
2493 ServiceBrowserCreate(
2494 dispatch_queue_t inQueue,
2495 uint32_t inInterfaceIndex,
2496 const char * inDomain,
2497 unsigned int inBrowseTimeSecs,
2498 Boolean inIncludeAWDL,
2499 ServiceBrowserRef * outBrowser );
2500 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2501 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2502 static void
2503 ServiceBrowserSetCallback(
2504 ServiceBrowserRef inBrowser,
2505 ServiceBrowserCallback_f inCallback,
2506 void * inContext );
2507 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2508 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2509
2510 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2511
2512 //===========================================================================================================================
2513 // main
2514 //===========================================================================================================================
2515
2516 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2517 static int \
2518 _PrintFExtension ## NAME ## Handler( \
2519 PrintFContext * inContext, \
2520 PrintFFormat * inFormat, \
2521 PrintFVAList * inArgs, \
2522 void * inUserContext )
2523
2524 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
2525 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
2526 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
2527 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
2528
2529 int main( int argc, const char **argv )
2530 {
2531 OSStatus err;
2532
2533 // Route DebugServices logging output to stderr.
2534
2535 dlog_control( "DebugServices:output=file;stderr" );
2536
2537 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler, NULL );
2538 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler, NULL );
2539 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler, NULL );
2540 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler, NULL );
2541 CLIInit( argc, argv );
2542 err = CLIParse( kGlobalOpts, kCLIFlags_None );
2543 if( err ) exit( 1 );
2544
2545 return( gExitCode );
2546 }
2547
2548 //===========================================================================================================================
2549 // VersionOptionCallback
2550 //===========================================================================================================================
2551
2552 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
2553 {
2554 const char * srcVers;
2555 #if( MDNSRESPONDER_PROJECT )
2556 char srcStr[ 16 ];
2557 #endif
2558
2559 Unused( inOption );
2560 Unused( inArg );
2561 Unused( inUnset );
2562
2563 #if( MDNSRESPONDER_PROJECT )
2564 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
2565 #else
2566 srcVers = DNSSDUTIL_SOURCE_VERSION;
2567 #endif
2568 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
2569
2570 return( kEndingErr );
2571 }
2572
2573 //===========================================================================================================================
2574 // BrowseCmd
2575 //===========================================================================================================================
2576
2577 typedef struct BrowseResolveOp BrowseResolveOp;
2578
2579 struct BrowseResolveOp
2580 {
2581 BrowseResolveOp * next; // Next resolve operation in list.
2582 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2583 char * fullName; // Full name of the service to resolve.
2584 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2585 };
2586
2587 typedef struct
2588 {
2589 DNSServiceRef mainRef; // Main sdRef for shared connection.
2590 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
2591 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
2592 const char * domain; // Domain for DNSServiceBrowse operation(s).
2593 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
2594 char ** serviceTypes; // Array of service types to browse for.
2595 size_t serviceTypesCount; // Count of array of service types to browse for.
2596 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
2597 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
2598 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
2599 Boolean printedHeader; // True if results header has been printed.
2600 Boolean doResolve; // True if service instances are to be resolved.
2601 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
2602
2603 } BrowseContext;
2604
2605 static void BrowsePrintPrologue( const BrowseContext *inContext );
2606 static void BrowseContextFree( BrowseContext *inContext );
2607 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
2608 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
2609 static void DNSSD_API
2610 BrowseCallback(
2611 DNSServiceRef inSDRef,
2612 DNSServiceFlags inFlags,
2613 uint32_t inInterfaceIndex,
2614 DNSServiceErrorType inError,
2615 const char * inName,
2616 const char * inRegType,
2617 const char * inDomain,
2618 void * inContext );
2619 static void DNSSD_API
2620 BrowseResolveCallback(
2621 DNSServiceRef inSDRef,
2622 DNSServiceFlags inFlags,
2623 uint32_t inInterfaceIndex,
2624 DNSServiceErrorType inError,
2625 const char * inFullName,
2626 const char * inHostname,
2627 uint16_t inPort,
2628 uint16_t inTXTLen,
2629 const unsigned char * inTXTPtr,
2630 void * inContext );
2631 static void DNSSD_API
2632 BrowseQueryRecordCallback(
2633 DNSServiceRef inSDRef,
2634 DNSServiceFlags inFlags,
2635 uint32_t inInterfaceIndex,
2636 DNSServiceErrorType inError,
2637 const char * inFullName,
2638 uint16_t inType,
2639 uint16_t inClass,
2640 uint16_t inRDataLen,
2641 const void * inRDataPtr,
2642 uint32_t inTTL,
2643 void * inContext );
2644
2645 static void BrowseCmd( void )
2646 {
2647 OSStatus err;
2648 size_t i;
2649 BrowseContext * context = NULL;
2650 dispatch_source_t signalSource = NULL;
2651 int useMainConnection;
2652
2653 // Set up SIGINT handler.
2654
2655 signal( SIGINT, SIG_IGN );
2656 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2657 require_noerr( err, exit );
2658 dispatch_resume( signalSource );
2659
2660 // Create context.
2661
2662 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
2663 require_action( context, exit, err = kNoMemoryErr );
2664
2665 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
2666 require_action( context->opRefs, exit, err = kNoMemoryErr );
2667 context->opRefsCount = gBrowse_ServiceTypesCount;
2668
2669 // Check command parameters.
2670
2671 if( gBrowse_TimeLimitSecs < 0 )
2672 {
2673 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
2674 err = kParamErr;
2675 goto exit;
2676 }
2677
2678 // Create main connection.
2679
2680 if( gConnectionOpt )
2681 {
2682 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2683 require_noerr_quiet( err, exit );
2684 useMainConnection = true;
2685 }
2686 else
2687 {
2688 useMainConnection = false;
2689 }
2690
2691 // Get flags.
2692
2693 context->flags = GetDNSSDFlagsFromOpts();
2694 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2695
2696 // Get interface.
2697
2698 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2699 require_noerr_quiet( err, exit );
2700
2701 // Set remaining parameters.
2702
2703 context->serviceTypes = gBrowse_ServiceTypes;
2704 context->serviceTypesCount = gBrowse_ServiceTypesCount;
2705 context->domain = gBrowse_Domain;
2706 context->doResolve = gBrowse_DoResolve ? true : false;
2707 context->timeLimitSecs = gBrowse_TimeLimitSecs;
2708 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
2709
2710 // Print prologue.
2711
2712 BrowsePrintPrologue( context );
2713
2714 // Start operation(s).
2715
2716 for( i = 0; i < context->serviceTypesCount; ++i )
2717 {
2718 DNSServiceRef sdRef;
2719
2720 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2721 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
2722 BrowseCallback, context );
2723 require_noerr( err, exit );
2724
2725 context->opRefs[ i ] = sdRef;
2726 if( !useMainConnection )
2727 {
2728 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
2729 require_noerr( err, exit );
2730 }
2731 }
2732
2733 // Set time limit.
2734
2735 if( context->timeLimitSecs > 0 )
2736 {
2737 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2738 kExitReason_TimeLimit, Exit );
2739 }
2740 dispatch_main();
2741
2742 exit:
2743 dispatch_source_forget( &signalSource );
2744 if( context ) BrowseContextFree( context );
2745 if( err ) exit( 1 );
2746 }
2747
2748 //===========================================================================================================================
2749 // BrowsePrintPrologue
2750 //===========================================================================================================================
2751
2752 static void BrowsePrintPrologue( const BrowseContext *inContext )
2753 {
2754 const int timeLimitSecs = inContext->timeLimitSecs;
2755 const char * const * ptr = (const char **) inContext->serviceTypes;
2756 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
2757 char ifName[ kInterfaceNameBufLen ];
2758
2759 InterfaceIndexToName( inContext->ifIndex, ifName );
2760
2761 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2762 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2763 FPrintF( stdout, "Service types: %s", *ptr++ );
2764 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
2765 FPrintF( stdout, "\n" );
2766 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2767 FPrintF( stdout, "Time limit: " );
2768 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2769 else FPrintF( stdout, "∞\n" );
2770 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2771 FPrintF( stdout, "---\n" );
2772 }
2773
2774 //===========================================================================================================================
2775 // BrowseContextFree
2776 //===========================================================================================================================
2777
2778 static void BrowseContextFree( BrowseContext *inContext )
2779 {
2780 size_t i;
2781
2782 for( i = 0; i < inContext->opRefsCount; ++i )
2783 {
2784 DNSServiceForget( &inContext->opRefs[ i ] );
2785 }
2786 if( inContext->serviceTypes )
2787 {
2788 _StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
2789 inContext->serviceTypes = NULL;
2790 inContext->serviceTypesCount = 0;
2791 }
2792 DNSServiceForget( &inContext->mainRef );
2793 free( inContext );
2794 }
2795
2796 //===========================================================================================================================
2797 // BrowseResolveOpCreate
2798 //===========================================================================================================================
2799
2800 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
2801 {
2802 OSStatus err;
2803 BrowseResolveOp * resolveOp;
2804
2805 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
2806 require_action( resolveOp, exit, err = kNoMemoryErr );
2807
2808 resolveOp->fullName = strdup( inFullName );
2809 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
2810
2811 resolveOp->interfaceIndex = inInterfaceIndex;
2812
2813 *outOp = resolveOp;
2814 resolveOp = NULL;
2815 err = kNoErr;
2816
2817 exit:
2818 if( resolveOp ) BrowseResolveOpFree( resolveOp );
2819 return( err );
2820 }
2821
2822 //===========================================================================================================================
2823 // BrowseResolveOpFree
2824 //===========================================================================================================================
2825
2826 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
2827 {
2828 DNSServiceForget( &inOp->sdRef );
2829 ForgetMem( &inOp->fullName );
2830 free( inOp );
2831 }
2832
2833 //===========================================================================================================================
2834 // BrowseCallback
2835 //===========================================================================================================================
2836
2837 static void DNSSD_API
2838 BrowseCallback(
2839 DNSServiceRef inSDRef,
2840 DNSServiceFlags inFlags,
2841 uint32_t inInterfaceIndex,
2842 DNSServiceErrorType inError,
2843 const char * inName,
2844 const char * inRegType,
2845 const char * inDomain,
2846 void * inContext )
2847 {
2848 BrowseContext * const context = (BrowseContext *) inContext;
2849 OSStatus err;
2850 BrowseResolveOp * newOp = NULL;
2851 BrowseResolveOp ** p;
2852 char fullName[ kDNSServiceMaxDomainName ];
2853 struct timeval now;
2854
2855 Unused( inSDRef );
2856
2857 gettimeofday( &now, NULL );
2858
2859 err = inError;
2860 require_noerr( err, exit );
2861
2862 if( !context->printedHeader )
2863 {
2864 FPrintF( stdout, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2865 context->printedHeader = true;
2866 }
2867 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2868 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2869
2870 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2871
2872 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2873 require_noerr( err, exit );
2874
2875 if( inFlags & kDNSServiceFlagsAdd )
2876 {
2877 DNSServiceRef sdRef;
2878 DNSServiceFlags flags;
2879
2880 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2881 require_noerr( err, exit );
2882
2883 if( context->mainRef )
2884 {
2885 sdRef = context->mainRef;
2886 flags = kDNSServiceFlagsShareConnection;
2887 }
2888 else
2889 {
2890 flags = 0;
2891 }
2892 if( context->doResolve )
2893 {
2894 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2895 NULL );
2896 require_noerr( err, exit );
2897 }
2898 else
2899 {
2900 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2901 BrowseQueryRecordCallback, NULL );
2902 require_noerr( err, exit );
2903 }
2904
2905 newOp->sdRef = sdRef;
2906 if( !context->mainRef )
2907 {
2908 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2909 require_noerr( err, exit );
2910 }
2911 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2912 *p = newOp;
2913 newOp = NULL;
2914 }
2915 else
2916 {
2917 BrowseResolveOp * resolveOp;
2918
2919 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2920 {
2921 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
2922 {
2923 break;
2924 }
2925 }
2926 if( resolveOp )
2927 {
2928 *p = resolveOp->next;
2929 BrowseResolveOpFree( resolveOp );
2930 }
2931 }
2932
2933 exit:
2934 if( newOp ) BrowseResolveOpFree( newOp );
2935 if( err ) exit( 1 );
2936 }
2937
2938 //===========================================================================================================================
2939 // BrowseQueryRecordCallback
2940 //===========================================================================================================================
2941
2942 static void DNSSD_API
2943 BrowseQueryRecordCallback(
2944 DNSServiceRef inSDRef,
2945 DNSServiceFlags inFlags,
2946 uint32_t inInterfaceIndex,
2947 DNSServiceErrorType inError,
2948 const char * inFullName,
2949 uint16_t inType,
2950 uint16_t inClass,
2951 uint16_t inRDataLen,
2952 const void * inRDataPtr,
2953 uint32_t inTTL,
2954 void * inContext )
2955 {
2956 OSStatus err;
2957 struct timeval now;
2958
2959 Unused( inSDRef );
2960 Unused( inClass );
2961 Unused( inTTL );
2962 Unused( inContext );
2963
2964 gettimeofday( &now, NULL );
2965
2966 err = inError;
2967 require_noerr( err, exit );
2968 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
2969
2970 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2971 &now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
2972 (size_t) inRDataLen );
2973
2974 exit:
2975 if( err ) exit( 1 );
2976 }
2977
2978 //===========================================================================================================================
2979 // BrowseResolveCallback
2980 //===========================================================================================================================
2981
2982 static void DNSSD_API
2983 BrowseResolveCallback(
2984 DNSServiceRef inSDRef,
2985 DNSServiceFlags inFlags,
2986 uint32_t inInterfaceIndex,
2987 DNSServiceErrorType inError,
2988 const char * inFullName,
2989 const char * inHostname,
2990 uint16_t inPort,
2991 uint16_t inTXTLen,
2992 const unsigned char * inTXTPtr,
2993 void * inContext )
2994 {
2995 struct timeval now;
2996 char errorStr[ 64 ];
2997
2998 Unused( inSDRef );
2999 Unused( inFlags );
3000 Unused( inContext );
3001
3002 gettimeofday( &now, NULL );
3003
3004 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3005
3006 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3007 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3008 if( inTXTLen == 1 )
3009 {
3010 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3011 }
3012 else
3013 {
3014 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3015 }
3016 }
3017
3018 //===========================================================================================================================
3019 // GetAddrInfoCmd
3020 //===========================================================================================================================
3021
3022 typedef struct
3023 {
3024 DNSServiceRef mainRef; // Main sdRef for shared connection.
3025 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3026 const char * name; // Hostname to resolve.
3027 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3028 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3029 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3030 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3031 Boolean printedHeader; // True if the results header has been printed.
3032 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3033 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3034 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3035
3036 } GetAddrInfoContext;
3037
3038 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3039 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3040 static void DNSSD_API
3041 GetAddrInfoCallback(
3042 DNSServiceRef inSDRef,
3043 DNSServiceFlags inFlags,
3044 uint32_t inInterfaceIndex,
3045 DNSServiceErrorType inError,
3046 const char * inHostname,
3047 const struct sockaddr * inSockAddr,
3048 uint32_t inTTL,
3049 void * inContext );
3050
3051 static void GetAddrInfoCmd( void )
3052 {
3053 OSStatus err;
3054 DNSServiceRef sdRef;
3055 GetAddrInfoContext * context = NULL;
3056 dispatch_source_t signalSource = NULL;
3057 int useMainConnection;
3058
3059 // Set up SIGINT handler.
3060
3061 signal( SIGINT, SIG_IGN );
3062 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3063 require_noerr( err, exit );
3064 dispatch_resume( signalSource );
3065
3066 // Check command parameters.
3067
3068 if( gGetAddrInfo_TimeLimitSecs < 0 )
3069 {
3070 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3071 err = kParamErr;
3072 goto exit;
3073 }
3074
3075 // Create context.
3076
3077 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3078 require_action( context, exit, err = kNoMemoryErr );
3079
3080 // Create main connection.
3081
3082 if( gConnectionOpt )
3083 {
3084 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3085 require_noerr_quiet( err, exit );
3086 useMainConnection = true;
3087 }
3088 else
3089 {
3090 useMainConnection = false;
3091 }
3092
3093 // Get flags.
3094
3095 context->flags = GetDNSSDFlagsFromOpts();
3096 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3097
3098 // Get interface.
3099
3100 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3101 require_noerr_quiet( err, exit );
3102
3103 // Set remaining parameters.
3104
3105 context->name = gGetAddrInfo_Name;
3106 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3107 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3108 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3109 if( gGetAddrInfo_OneShot )
3110 {
3111 context->oneShotMode = true;
3112 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3113 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3114 }
3115
3116 // Print prologue.
3117
3118 GetAddrInfoPrintPrologue( context );
3119
3120 // Start operation.
3121
3122 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3123 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3124 GetAddrInfoCallback, context );
3125 require_noerr( err, exit );
3126
3127 context->opRef = sdRef;
3128 if( !useMainConnection )
3129 {
3130 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3131 require_noerr( err, exit );
3132 }
3133
3134 // Set time limit.
3135
3136 if( context->timeLimitSecs > 0 )
3137 {
3138 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3139 kExitReason_TimeLimit, Exit );
3140 }
3141 dispatch_main();
3142
3143 exit:
3144 dispatch_source_forget( &signalSource );
3145 if( context ) GetAddrInfoContextFree( context );
3146 if( err ) exit( 1 );
3147 }
3148
3149 //===========================================================================================================================
3150 // GetAddrInfoPrintPrologue
3151 //===========================================================================================================================
3152
3153 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3154 {
3155 const int timeLimitSecs = inContext->timeLimitSecs;
3156 char ifName[ kInterfaceNameBufLen ];
3157
3158 InterfaceIndexToName( inContext->ifIndex, ifName );
3159
3160 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3161 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3162 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3163 FPrintF( stdout, "Name: %s\n", inContext->name );
3164 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3165 FPrintF( stdout, "Time limit: " );
3166 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3167 else FPrintF( stdout, "∞\n" );
3168 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3169 FPrintF( stdout, "---\n" );
3170 }
3171
3172 //===========================================================================================================================
3173 // GetAddrInfoContextFree
3174 //===========================================================================================================================
3175
3176 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3177 {
3178 DNSServiceForget( &inContext->opRef );
3179 DNSServiceForget( &inContext->mainRef );
3180 free( inContext );
3181 }
3182
3183 //===========================================================================================================================
3184 // GetAddrInfoCallback
3185 //===========================================================================================================================
3186
3187 static void DNSSD_API
3188 GetAddrInfoCallback(
3189 DNSServiceRef inSDRef,
3190 DNSServiceFlags inFlags,
3191 uint32_t inInterfaceIndex,
3192 DNSServiceErrorType inError,
3193 const char * inHostname,
3194 const struct sockaddr * inSockAddr,
3195 uint32_t inTTL,
3196 void * inContext )
3197 {
3198 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3199 struct timeval now;
3200 OSStatus err;
3201 const char * addrStr;
3202 char addrStrBuf[ kSockAddrStringMaxSize ];
3203
3204 Unused( inSDRef );
3205
3206 gettimeofday( &now, NULL );
3207
3208 switch( inError )
3209 {
3210 case kDNSServiceErr_NoError:
3211 case kDNSServiceErr_NoSuchRecord:
3212 err = kNoErr;
3213 break;
3214
3215 case kDNSServiceErr_Timeout:
3216 Exit( kExitReason_Timeout );
3217
3218 default:
3219 err = inError;
3220 goto exit;
3221 }
3222
3223 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3224 {
3225 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3226 err = kTypeErr;
3227 goto exit;
3228 }
3229
3230 if( !inError )
3231 {
3232 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3233 require_noerr( err, exit );
3234 addrStr = addrStrBuf;
3235 }
3236 else
3237 {
3238 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3239 }
3240
3241 if( !context->printedHeader )
3242 {
3243 FPrintF( stdout, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3244 context->printedHeader = true;
3245 }
3246 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3247 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3248
3249 if( context->oneShotMode )
3250 {
3251 if( inFlags & kDNSServiceFlagsAdd )
3252 {
3253 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3254 else context->needIPv6 = false;
3255 }
3256 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3257 {
3258 Exit( kExitReason_OneShotDone );
3259 }
3260 }
3261
3262 exit:
3263 if( err ) exit( 1 );
3264 }
3265
3266 //===========================================================================================================================
3267 // QueryRecordCmd
3268 //===========================================================================================================================
3269
3270 typedef struct
3271 {
3272 DNSServiceRef mainRef; // Main sdRef for shared connection.
3273 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3274 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3275 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3276 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3277 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3278 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3279 Boolean printedHeader; // True if the results header was printed.
3280 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3281 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3282 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3283
3284 } QueryRecordContext;
3285
3286 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3287 static void QueryRecordContextFree( QueryRecordContext *inContext );
3288 static void DNSSD_API
3289 QueryRecordCallback(
3290 DNSServiceRef inSDRef,
3291 DNSServiceFlags inFlags,
3292 uint32_t inInterfaceIndex,
3293 DNSServiceErrorType inError,
3294 const char * inFullName,
3295 uint16_t inType,
3296 uint16_t inClass,
3297 uint16_t inRDataLen,
3298 const void * inRDataPtr,
3299 uint32_t inTTL,
3300 void * inContext );
3301
3302 static void QueryRecordCmd( void )
3303 {
3304 OSStatus err;
3305 DNSServiceRef sdRef;
3306 QueryRecordContext * context = NULL;
3307 dispatch_source_t signalSource = NULL;
3308 int useMainConnection;
3309
3310 // Set up SIGINT handler.
3311
3312 signal( SIGINT, SIG_IGN );
3313 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3314 require_noerr( err, exit );
3315 dispatch_resume( signalSource );
3316
3317 // Create context.
3318
3319 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3320 require_action( context, exit, err = kNoMemoryErr );
3321
3322 // Check command parameters.
3323
3324 if( gQueryRecord_TimeLimitSecs < 0 )
3325 {
3326 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3327 err = kParamErr;
3328 goto exit;
3329 }
3330
3331 // Create main connection.
3332
3333 if( gConnectionOpt )
3334 {
3335 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3336 require_noerr_quiet( err, exit );
3337 useMainConnection = true;
3338 }
3339 else
3340 {
3341 useMainConnection = false;
3342 }
3343
3344 // Get flags.
3345
3346 context->flags = GetDNSSDFlagsFromOpts();
3347 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3348
3349 // Get interface.
3350
3351 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3352 require_noerr_quiet( err, exit );
3353
3354 // Get record type.
3355
3356 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3357 require_noerr( err, exit );
3358
3359 // Set remaining parameters.
3360
3361 context->recordName = gQueryRecord_Name;
3362 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3363 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3364 context->printRawRData = gQueryRecord_RawRData ? true : false;
3365
3366 // Print prologue.
3367
3368 QueryRecordPrintPrologue( context );
3369
3370 // Start operation.
3371
3372 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3373 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3374 kDNSServiceClass_IN, QueryRecordCallback, context );
3375 require_noerr( err, exit );
3376
3377 context->opRef = sdRef;
3378 if( !useMainConnection )
3379 {
3380 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3381 require_noerr( err, exit );
3382 }
3383
3384 // Set time limit.
3385
3386 if( context->timeLimitSecs > 0 )
3387 {
3388 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3389 Exit );
3390 }
3391 dispatch_main();
3392
3393 exit:
3394 dispatch_source_forget( &signalSource );
3395 if( context ) QueryRecordContextFree( context );
3396 if( err ) exit( 1 );
3397 }
3398
3399 //===========================================================================================================================
3400 // QueryRecordContextFree
3401 //===========================================================================================================================
3402
3403 static void QueryRecordContextFree( QueryRecordContext *inContext )
3404 {
3405 DNSServiceForget( &inContext->opRef );
3406 DNSServiceForget( &inContext->mainRef );
3407 free( inContext );
3408 }
3409
3410 //===========================================================================================================================
3411 // QueryRecordPrintPrologue
3412 //===========================================================================================================================
3413
3414 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3415 {
3416 const int timeLimitSecs = inContext->timeLimitSecs;
3417 char ifName[ kInterfaceNameBufLen ];
3418
3419 InterfaceIndexToName( inContext->ifIndex, ifName );
3420
3421 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3422 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3423 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3424 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3425 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3426 FPrintF( stdout, "Time limit: " );
3427 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3428 else FPrintF( stdout, "∞\n" );
3429 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3430 FPrintF( stdout, "---\n" );
3431
3432 }
3433
3434 //===========================================================================================================================
3435 // QueryRecordCallback
3436 //===========================================================================================================================
3437
3438 static void DNSSD_API
3439 QueryRecordCallback(
3440 DNSServiceRef inSDRef,
3441 DNSServiceFlags inFlags,
3442 uint32_t inInterfaceIndex,
3443 DNSServiceErrorType inError,
3444 const char * inFullName,
3445 uint16_t inType,
3446 uint16_t inClass,
3447 uint16_t inRDataLen,
3448 const void * inRDataPtr,
3449 uint32_t inTTL,
3450 void * inContext )
3451 {
3452 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3453 struct timeval now;
3454 OSStatus err;
3455 char * rdataStr = NULL;
3456
3457 Unused( inSDRef );
3458
3459 gettimeofday( &now, NULL );
3460
3461 switch( inError )
3462 {
3463 case kDNSServiceErr_NoError:
3464 case kDNSServiceErr_NoSuchRecord:
3465 err = kNoErr;
3466 break;
3467
3468 case kDNSServiceErr_Timeout:
3469 Exit( kExitReason_Timeout );
3470
3471 default:
3472 err = inError;
3473 goto exit;
3474 }
3475
3476 if( inError != kDNSServiceErr_NoSuchRecord )
3477 {
3478 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
3479 if( !rdataStr )
3480 {
3481 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
3482 require_action( rdataStr, exit, err = kNoMemoryErr );
3483 }
3484 }
3485
3486 if( !context->printedHeader )
3487 {
3488 FPrintF( stdout, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3489 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3490 context->printedHeader = true;
3491 }
3492 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3493 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
3494 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL,
3495 rdataStr ? rdataStr : kNoSuchRecordStr );
3496
3497 if( context->oneShotMode )
3498 {
3499 if( ( inFlags & kDNSServiceFlagsAdd ) &&
3500 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
3501 {
3502 context->gotRecord = true;
3503 }
3504 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
3505 }
3506
3507 exit:
3508 FreeNullSafe( rdataStr );
3509 if( err ) exit( 1 );
3510 }
3511
3512 //===========================================================================================================================
3513 // RegisterCmd
3514 //===========================================================================================================================
3515
3516 typedef struct
3517 {
3518 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
3519 uint8_t * dataPtr; // Record data.
3520 size_t dataLen; // Record data length.
3521 uint32_t ttl; // Record TTL value.
3522 uint16_t type; // Record type.
3523
3524 } ExtraRecord;
3525
3526 typedef struct
3527 {
3528 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
3529 const char * name; // Service name argument for DNSServiceRegister().
3530 const char * type; // Service type argument for DNSServiceRegister().
3531 const char * domain; // Domain in which advertise the service.
3532 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
3533 size_t txtLen; // Service TXT record data len.
3534 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
3535 size_t extraRecordsCount; // Number of extra records.
3536 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
3537 size_t updateTXTLen; // Length of record data for TXT record update.
3538 uint32_t updateTTL; // TTL of updated TXT record.
3539 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
3540 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
3541 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
3542 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3543 uint16_t port; // Service instance's port number.
3544 Boolean printedHeader; // True if results header was printed.
3545 Boolean didRegister; // True if service was registered.
3546
3547 } RegisterContext;
3548
3549 static void RegisterPrintPrologue( const RegisterContext *inContext );
3550 static void RegisterContextFree( RegisterContext *inContext );
3551 static void DNSSD_API
3552 RegisterCallback(
3553 DNSServiceRef inSDRef,
3554 DNSServiceFlags inFlags,
3555 DNSServiceErrorType inError,
3556 const char * inName,
3557 const char * inType,
3558 const char * inDomain,
3559 void * inContext );
3560 static void RegisterUpdate( void *inContext );
3561
3562 static void RegisterCmd( void )
3563 {
3564 OSStatus err;
3565 RegisterContext * context = NULL;
3566 dispatch_source_t signalSource = NULL;
3567
3568 // Set up SIGINT handler.
3569
3570 signal( SIGINT, SIG_IGN );
3571 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3572 require_noerr( err, exit );
3573 dispatch_resume( signalSource );
3574
3575 // Create context.
3576
3577 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
3578 require_action( context, exit, err = kNoMemoryErr );
3579
3580 // Check command parameters.
3581
3582 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
3583 {
3584 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
3585 err = kParamErr;
3586 goto exit;
3587 }
3588
3589 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
3590 {
3591 FPrintF( stderr, "There are missing additional record parameters.\n" );
3592 err = kParamErr;
3593 goto exit;
3594 }
3595
3596 // Get flags.
3597
3598 context->flags = GetDNSSDFlagsFromOpts();
3599
3600 // Get interface index.
3601
3602 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3603 require_noerr_quiet( err, exit );
3604
3605 // Get TXT record data.
3606
3607 if( gRegister_TXT )
3608 {
3609 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
3610 require_noerr_quiet( err, exit );
3611 }
3612
3613 // Set remaining parameters.
3614
3615 context->name = gRegister_Name;
3616 context->type = gRegister_Type;
3617 context->domain = gRegister_Domain;
3618 context->port = (uint16_t) gRegister_Port;
3619 context->lifetimeMs = gRegister_LifetimeMs;
3620
3621 if( gAddRecord_TypesCount > 0 )
3622 {
3623 size_t i;
3624
3625 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
3626 require_action( context, exit, err = kNoMemoryErr );
3627 context->extraRecordsCount = gAddRecord_TypesCount;
3628
3629 for( i = 0; i < gAddRecord_TypesCount; ++i )
3630 {
3631 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
3632
3633 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
3634 require_noerr( err, exit );
3635
3636 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
3637 if( err )
3638 {
3639 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
3640 err = kParamErr;
3641 goto exit;
3642 }
3643
3644 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
3645 require_noerr_quiet( err, exit );
3646 }
3647 }
3648
3649 if( gUpdateRecord_Data )
3650 {
3651 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
3652 require_noerr_quiet( err, exit );
3653
3654 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
3655 context->updateDelayMs = gUpdateRecord_DelayMs;
3656 }
3657
3658 // Print prologue.
3659
3660 RegisterPrintPrologue( context );
3661
3662 // Start operation.
3663
3664 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
3665 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
3666 RegisterCallback, context );
3667 ForgetMem( &context->txtPtr );
3668 require_noerr( err, exit );
3669
3670 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3671 require_noerr( err, exit );
3672
3673 dispatch_main();
3674
3675 exit:
3676 dispatch_source_forget( &signalSource );
3677 if( context ) RegisterContextFree( context );
3678 if( err ) exit( 1 );
3679 }
3680
3681 //===========================================================================================================================
3682 // RegisterPrintPrologue
3683 //===========================================================================================================================
3684
3685 static void RegisterPrintPrologue( const RegisterContext *inContext )
3686 {
3687 size_t i;
3688 int infinite;
3689 char ifName[ kInterfaceNameBufLen ];
3690
3691 InterfaceIndexToName( inContext->ifIndex, ifName );
3692
3693 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3694 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3695 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
3696 FPrintF( stdout, "Type: %s\n", inContext->type );
3697 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3698 FPrintF( stdout, "Port: %u\n", inContext->port );
3699 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
3700 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3701 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3702 if( inContext->updateTXTPtr )
3703 {
3704 FPrintF( stdout, "\nUpdate record:\n" );
3705 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
3706 FPrintF( stdout, " TTL: %u%?s\n",
3707 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3708 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
3709 }
3710 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
3711 for( i = 0; i < inContext->extraRecordsCount; ++i )
3712 {
3713 const ExtraRecord * record = &inContext->extraRecords[ i ];
3714
3715 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
3716 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
3717 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
3718 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
3719 }
3720 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3721 FPrintF( stdout, "---\n" );
3722 }
3723
3724 //===========================================================================================================================
3725 // RegisterContextFree
3726 //===========================================================================================================================
3727
3728 static void RegisterContextFree( RegisterContext *inContext )
3729 {
3730 ExtraRecord * record;
3731 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
3732
3733 DNSServiceForget( &inContext->opRef );
3734 ForgetMem( &inContext->txtPtr );
3735 for( record = inContext->extraRecords; record < end; ++record )
3736 {
3737 check( !record->recordRef );
3738 ForgetMem( &record->dataPtr );
3739 }
3740 ForgetMem( &inContext->extraRecords );
3741 ForgetMem( &inContext->updateTXTPtr );
3742 free( inContext );
3743 }
3744
3745 //===========================================================================================================================
3746 // RegisterCallback
3747 //===========================================================================================================================
3748
3749 static void DNSSD_API
3750 RegisterCallback(
3751 DNSServiceRef inSDRef,
3752 DNSServiceFlags inFlags,
3753 DNSServiceErrorType inError,
3754 const char * inName,
3755 const char * inType,
3756 const char * inDomain,
3757 void * inContext )
3758 {
3759 RegisterContext * const context = (RegisterContext *) inContext;
3760 OSStatus err;
3761 struct timeval now;
3762
3763 Unused( inSDRef );
3764
3765 gettimeofday( &now, NULL );
3766
3767 if( !context->printedHeader )
3768 {
3769 FPrintF( stdout, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3770 context->printedHeader = true;
3771 }
3772 FPrintF( stdout, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
3773
3774 require_noerr_action_quiet( inError, exit, err = inError );
3775
3776 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
3777 {
3778 context->didRegister = true;
3779 if( context->updateTXTPtr )
3780 {
3781 if( context->updateDelayMs > 0 )
3782 {
3783 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3784 context, RegisterUpdate );
3785 }
3786 else
3787 {
3788 RegisterUpdate( context );
3789 }
3790 }
3791 if( context->extraRecordsCount > 0 )
3792 {
3793 ExtraRecord * record;
3794 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
3795
3796 for( record = context->extraRecords; record < end; ++record )
3797 {
3798 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
3799 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
3800 require_noerr( err, exit );
3801 }
3802 }
3803 if( context->lifetimeMs == 0 )
3804 {
3805 Exit( kExitReason_TimeLimit );
3806 }
3807 else if( context->lifetimeMs > 0 )
3808 {
3809 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3810 kExitReason_TimeLimit, Exit );
3811 }
3812 }
3813 err = kNoErr;
3814
3815 exit:
3816 if( err ) exit( 1 );
3817 }
3818
3819 //===========================================================================================================================
3820 // RegisterUpdate
3821 //===========================================================================================================================
3822
3823 static void RegisterUpdate( void *inContext )
3824 {
3825 OSStatus err;
3826 RegisterContext * const context = (RegisterContext *) inContext;
3827
3828 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
3829 context->updateTTL );
3830 require_noerr( err, exit );
3831
3832 exit:
3833 if( err ) exit( 1 );
3834 }
3835
3836 //===========================================================================================================================
3837 // RegisterRecordCmd
3838 //===========================================================================================================================
3839
3840 typedef struct
3841 {
3842 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
3843 DNSRecordRef recordRef; // Registered record reference.
3844 const char * recordName; // Name of resource record.
3845 uint8_t * dataPtr; // Pointer to resource record data.
3846 size_t dataLen; // Length of resource record data.
3847 uint32_t ttl; // TTL value of resource record in seconds.
3848 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3849 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3850 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3851 uint16_t recordType; // Resource record type.
3852 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3853 size_t updateDataLen; // Length of data for record update.
3854 uint32_t updateTTL; // TTL for updated record.
3855 int updateDelayMs; // Post-registration record update delay in milliseconds.
3856 Boolean didRegister; // True if the record was registered.
3857
3858 } RegisterRecordContext;
3859
3860 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3861 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3862 static void DNSSD_API
3863 RegisterRecordCallback(
3864 DNSServiceRef inSDRef,
3865 DNSRecordRef inRecordRef,
3866 DNSServiceFlags inFlags,
3867 DNSServiceErrorType inError,
3868 void * inContext );
3869 static void RegisterRecordUpdate( void *inContext );
3870
3871 static void RegisterRecordCmd( void )
3872 {
3873 OSStatus err;
3874 RegisterRecordContext * context = NULL;
3875 dispatch_source_t signalSource = NULL;
3876
3877 // Set up SIGINT handler.
3878
3879 signal( SIGINT, SIG_IGN );
3880 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3881 require_noerr( err, exit );
3882 dispatch_resume( signalSource );
3883
3884 // Create context.
3885
3886 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3887 require_action( context, exit, err = kNoMemoryErr );
3888
3889 // Create connection.
3890
3891 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3892 require_noerr_quiet( err, exit );
3893
3894 // Get flags.
3895
3896 context->flags = GetDNSSDFlagsFromOpts();
3897
3898 // Get interface.
3899
3900 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3901 require_noerr_quiet( err, exit );
3902
3903 // Get record type.
3904
3905 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3906 require_noerr( err, exit );
3907
3908 // Get record data.
3909
3910 if( gRegisterRecord_Data )
3911 {
3912 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3913 require_noerr_quiet( err, exit );
3914 }
3915
3916 // Set remaining parameters.
3917
3918 context->recordName = gRegisterRecord_Name;
3919 context->ttl = (uint32_t) gRegisterRecord_TTL;
3920 context->lifetimeMs = gRegisterRecord_LifetimeMs;
3921
3922 // Get update data.
3923
3924 if( gRegisterRecord_UpdateData )
3925 {
3926 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
3927 require_noerr_quiet( err, exit );
3928
3929 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
3930 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
3931 }
3932
3933 // Print prologue.
3934
3935 RegisterRecordPrintPrologue( context );
3936
3937 // Start operation.
3938
3939 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
3940 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
3941 context->ttl, RegisterRecordCallback, context );
3942 if( err )
3943 {
3944 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
3945 goto exit;
3946 }
3947
3948 dispatch_main();
3949
3950 exit:
3951 dispatch_source_forget( &signalSource );
3952 if( context ) RegisterRecordContextFree( context );
3953 if( err ) exit( 1 );
3954 }
3955
3956 //===========================================================================================================================
3957 // RegisterRecordPrintPrologue
3958 //===========================================================================================================================
3959
3960 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
3961 {
3962 int infinite;
3963 char ifName[ kInterfaceNameBufLen ];
3964
3965 InterfaceIndexToName( inContext->ifIndex, ifName );
3966
3967 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3968 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3969 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3970 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3971 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
3972 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
3973 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3974 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3975 if( inContext->updateDataPtr )
3976 {
3977 FPrintF( stdout, "\nUpdate record:\n" );
3978 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
3979 FPrintF( stdout, " TTL: %u%?s\n",
3980 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3981 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
3982 }
3983 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3984 FPrintF( stdout, "---\n" );
3985 }
3986
3987 //===========================================================================================================================
3988 // RegisterRecordContextFree
3989 //===========================================================================================================================
3990
3991 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
3992 {
3993 DNSServiceForget( &inContext->conRef );
3994 ForgetMem( &inContext->dataPtr );
3995 ForgetMem( &inContext->updateDataPtr );
3996 free( inContext );
3997 }
3998
3999 //===========================================================================================================================
4000 // RegisterRecordCallback
4001 //===========================================================================================================================
4002
4003 static void
4004 RegisterRecordCallback(
4005 DNSServiceRef inSDRef,
4006 DNSRecordRef inRecordRef,
4007 DNSServiceFlags inFlags,
4008 DNSServiceErrorType inError,
4009 void * inContext )
4010 {
4011 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4012 struct timeval now;
4013
4014 Unused( inSDRef );
4015 Unused( inRecordRef );
4016 Unused( inFlags );
4017 Unused( context );
4018
4019 gettimeofday( &now, NULL );
4020 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4021
4022 if( !context->didRegister && !inError )
4023 {
4024 context->didRegister = true;
4025 if( context->updateDataPtr )
4026 {
4027 if( context->updateDelayMs > 0 )
4028 {
4029 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4030 context, RegisterRecordUpdate );
4031 }
4032 else
4033 {
4034 RegisterRecordUpdate( context );
4035 }
4036 }
4037 if( context->lifetimeMs == 0 )
4038 {
4039 Exit( kExitReason_TimeLimit );
4040 }
4041 else if( context->lifetimeMs > 0 )
4042 {
4043 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4044 kExitReason_TimeLimit, Exit );
4045 }
4046 }
4047 }
4048
4049 //===========================================================================================================================
4050 // RegisterRecordUpdate
4051 //===========================================================================================================================
4052
4053 static void RegisterRecordUpdate( void *inContext )
4054 {
4055 OSStatus err;
4056 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4057
4058 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4059 context->updateDataPtr, context->updateTTL );
4060 require_noerr( err, exit );
4061
4062 exit:
4063 if( err ) exit( 1 );
4064 }
4065
4066 //===========================================================================================================================
4067 // ResolveCmd
4068 //===========================================================================================================================
4069
4070 typedef struct
4071 {
4072 DNSServiceRef mainRef; // Main sdRef for shared connections.
4073 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4074 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4075 const char * name; // Service name argument for DNSServiceResolve().
4076 const char * type; // Service type argument for DNSServiceResolve().
4077 const char * domain; // Domain argument for DNSServiceResolve().
4078 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4079 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4080
4081 } ResolveContext;
4082
4083 static void ResolvePrintPrologue( const ResolveContext *inContext );
4084 static void ResolveContextFree( ResolveContext *inContext );
4085 static void DNSSD_API
4086 ResolveCallback(
4087 DNSServiceRef inSDRef,
4088 DNSServiceFlags inFlags,
4089 uint32_t inInterfaceIndex,
4090 DNSServiceErrorType inError,
4091 const char * inFullName,
4092 const char * inHostname,
4093 uint16_t inPort,
4094 uint16_t inTXTLen,
4095 const unsigned char * inTXTPtr,
4096 void * inContext );
4097
4098 static void ResolveCmd( void )
4099 {
4100 OSStatus err;
4101 DNSServiceRef sdRef;
4102 ResolveContext * context = NULL;
4103 dispatch_source_t signalSource = NULL;
4104 int useMainConnection;
4105
4106 // Set up SIGINT handler.
4107
4108 signal( SIGINT, SIG_IGN );
4109 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4110 require_noerr( err, exit );
4111 dispatch_resume( signalSource );
4112
4113 // Create context.
4114
4115 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4116 require_action( context, exit, err = kNoMemoryErr );
4117
4118 // Check command parameters.
4119
4120 if( gResolve_TimeLimitSecs < 0 )
4121 {
4122 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4123 err = kParamErr;
4124 goto exit;
4125 }
4126
4127 // Create main connection.
4128
4129 if( gConnectionOpt )
4130 {
4131 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4132 require_noerr_quiet( err, exit );
4133 useMainConnection = true;
4134 }
4135 else
4136 {
4137 useMainConnection = false;
4138 }
4139
4140 // Get flags.
4141
4142 context->flags = GetDNSSDFlagsFromOpts();
4143 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4144
4145 // Get interface index.
4146
4147 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4148 require_noerr_quiet( err, exit );
4149
4150 // Set remaining parameters.
4151
4152 context->name = gResolve_Name;
4153 context->type = gResolve_Type;
4154 context->domain = gResolve_Domain;
4155 context->timeLimitSecs = gResolve_TimeLimitSecs;
4156
4157 // Print prologue.
4158
4159 ResolvePrintPrologue( context );
4160
4161 // Start operation.
4162
4163 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4164 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4165 ResolveCallback, NULL );
4166 require_noerr( err, exit );
4167
4168 context->opRef = sdRef;
4169 if( !useMainConnection )
4170 {
4171 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4172 require_noerr( err, exit );
4173 }
4174
4175 // Set time limit.
4176
4177 if( context->timeLimitSecs > 0 )
4178 {
4179 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4180 kExitReason_TimeLimit, Exit );
4181 }
4182 dispatch_main();
4183
4184 exit:
4185 dispatch_source_forget( &signalSource );
4186 if( context ) ResolveContextFree( context );
4187 if( err ) exit( 1 );
4188 }
4189
4190 //===========================================================================================================================
4191 // ReconfirmCmd
4192 //===========================================================================================================================
4193
4194 static void ReconfirmCmd( void )
4195 {
4196 OSStatus err;
4197 uint8_t * rdataPtr = NULL;
4198 size_t rdataLen = 0;
4199 DNSServiceFlags flags;
4200 uint32_t ifIndex;
4201 uint16_t type, class;
4202 char ifName[ kInterfaceNameBufLen ];
4203
4204 // Get flags.
4205
4206 flags = GetDNSSDFlagsFromOpts();
4207
4208 // Get interface index.
4209
4210 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4211 require_noerr_quiet( err, exit );
4212
4213 // Get record type.
4214
4215 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4216 require_noerr( err, exit );
4217
4218 // Get record data.
4219
4220 if( gReconfirmRecord_Data )
4221 {
4222 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4223 require_noerr_quiet( err, exit );
4224 }
4225
4226 // Get record class.
4227
4228 if( gReconfirmRecord_Class )
4229 {
4230 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4231 require_noerr( err, exit );
4232 }
4233 else
4234 {
4235 class = kDNSServiceClass_IN;
4236 }
4237
4238 // Print prologue.
4239
4240 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4241 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4242 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4243 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4244 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4245 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4246 FPrintF( stdout, "---\n" );
4247
4248 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4249 FPrintF( stdout, "Error: %#m\n", err );
4250
4251 exit:
4252 FreeNullSafe( rdataPtr );
4253 if( err ) exit( 1 );
4254 }
4255
4256 //===========================================================================================================================
4257 // ResolvePrintPrologue
4258 //===========================================================================================================================
4259
4260 static void ResolvePrintPrologue( const ResolveContext *inContext )
4261 {
4262 const int timeLimitSecs = inContext->timeLimitSecs;
4263 char ifName[ kInterfaceNameBufLen ];
4264
4265 InterfaceIndexToName( inContext->ifIndex, ifName );
4266
4267 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4268 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4269 FPrintF( stdout, "Name: %s\n", inContext->name );
4270 FPrintF( stdout, "Type: %s\n", inContext->type );
4271 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4272 FPrintF( stdout, "Time limit: " );
4273 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4274 else FPrintF( stdout, "∞\n" );
4275 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4276 FPrintF( stdout, "---\n" );
4277 }
4278
4279 //===========================================================================================================================
4280 // ResolveContextFree
4281 //===========================================================================================================================
4282
4283 static void ResolveContextFree( ResolveContext *inContext )
4284 {
4285 DNSServiceForget( &inContext->opRef );
4286 DNSServiceForget( &inContext->mainRef );
4287 free( inContext );
4288 }
4289
4290 //===========================================================================================================================
4291 // ResolveCallback
4292 //===========================================================================================================================
4293
4294 static void DNSSD_API
4295 ResolveCallback(
4296 DNSServiceRef inSDRef,
4297 DNSServiceFlags inFlags,
4298 uint32_t inInterfaceIndex,
4299 DNSServiceErrorType inError,
4300 const char * inFullName,
4301 const char * inHostname,
4302 uint16_t inPort,
4303 uint16_t inTXTLen,
4304 const unsigned char * inTXTPtr,
4305 void * inContext )
4306 {
4307 struct timeval now;
4308 char errorStr[ 64 ];
4309
4310 Unused( inSDRef );
4311 Unused( inFlags );
4312 Unused( inContext );
4313
4314 gettimeofday( &now, NULL );
4315
4316 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4317
4318 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4319 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4320 if( inTXTLen == 1 )
4321 {
4322 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4323 }
4324 else
4325 {
4326 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4327 }
4328 }
4329
4330 //===========================================================================================================================
4331 // GetAddrInfoPOSIXCmd
4332 //===========================================================================================================================
4333
4334 #define AddressFamilyStr( X ) ( \
4335 ( (X) == AF_INET ) ? "inet" : \
4336 ( (X) == AF_INET6 ) ? "inet6" : \
4337 ( (X) == AF_UNSPEC ) ? "unspec" : \
4338 "???" )
4339
4340 typedef struct
4341 {
4342 unsigned int flag;
4343 const char * str;
4344
4345 } FlagStringPair;
4346
4347 #define CaseFlagStringify( X ) { (X), # X }
4348
4349 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4350 {
4351 #if( defined( AI_UNUSABLE ) )
4352 CaseFlagStringify( AI_UNUSABLE ),
4353 #endif
4354 CaseFlagStringify( AI_NUMERICSERV ),
4355 CaseFlagStringify( AI_V4MAPPED ),
4356 CaseFlagStringify( AI_ADDRCONFIG ),
4357 #if( defined( AI_V4MAPPED_CFG ) )
4358 CaseFlagStringify( AI_V4MAPPED_CFG ),
4359 #endif
4360 CaseFlagStringify( AI_ALL ),
4361 CaseFlagStringify( AI_NUMERICHOST ),
4362 CaseFlagStringify( AI_CANONNAME ),
4363 CaseFlagStringify( AI_PASSIVE ),
4364 { 0, NULL }
4365 };
4366
4367 static void GetAddrInfoPOSIXCmd( void )
4368 {
4369 OSStatus err;
4370 struct addrinfo hints;
4371 struct timeval now;
4372 const struct addrinfo * addrInfo;
4373 struct addrinfo * addrInfoList = NULL;
4374 const FlagStringPair * pair;
4375
4376 memset( &hints, 0, sizeof( hints ) );
4377 hints.ai_socktype = SOCK_STREAM;
4378
4379 // Set hints address family.
4380
4381 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4382 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4383 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4384 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4385 else
4386 {
4387 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4388 err = kParamErr;
4389 goto exit;
4390 }
4391
4392 // Set hints flags.
4393
4394 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4395 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4396 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4397 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4398 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4399 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4400 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4401 #if( defined( AI_V4MAPPED_CFG ) )
4402 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4403 #endif
4404 #if( defined( AI_DEFAULT ) )
4405 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4406 #endif
4407 #if( defined( AI_UNUSABLE ) )
4408 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4409 #endif
4410
4411 // Print prologue.
4412
4413 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4414 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4415 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4416 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4417 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4418 {
4419 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4420 }
4421 FPrintF( stdout, ">\n" );
4422 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4423 FPrintF( stdout, "---\n" );
4424
4425 // Call getaddrinfo().
4426
4427 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4428 gettimeofday( &now, NULL );
4429 if( err )
4430 {
4431 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4432 }
4433 else
4434 {
4435 int addrCount = 0;
4436
4437 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4438
4439 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4440 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4441 {
4442 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4443 }
4444 }
4445 FPrintF( stdout, "---\n" );
4446 FPrintF( stdout, "End time: %{du:time}\n", &now );
4447
4448 exit:
4449 if( addrInfoList ) freeaddrinfo( addrInfoList );
4450 if( err ) exit( 1 );
4451 }
4452
4453 //===========================================================================================================================
4454 // ReverseLookupCmd
4455 //===========================================================================================================================
4456
4457 #define kIP6ARPADomainStr "ip6.arpa."
4458
4459 static void ReverseLookupCmd( void )
4460 {
4461 OSStatus err;
4462 QueryRecordContext * context = NULL;
4463 DNSServiceRef sdRef;
4464 dispatch_source_t signalSource = NULL;
4465 uint32_t ipv4Addr;
4466 uint8_t ipv6Addr[ 16 ];
4467 char recordName[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr ) ];
4468 int useMainConnection;
4469 const char * endPtr;
4470
4471 // Set up SIGINT handler.
4472
4473 signal( SIGINT, SIG_IGN );
4474 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4475 require_noerr( err, exit );
4476 dispatch_resume( signalSource );
4477
4478 // Create context.
4479
4480 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
4481 require_action( context, exit, err = kNoMemoryErr );
4482
4483 // Check command parameters.
4484
4485 if( gReverseLookup_TimeLimitSecs < 0 )
4486 {
4487 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
4488 err = kParamErr;
4489 goto exit;
4490 }
4491
4492 // Create main connection.
4493
4494 if( gConnectionOpt )
4495 {
4496 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4497 require_noerr_quiet( err, exit );
4498 useMainConnection = true;
4499 }
4500 else
4501 {
4502 useMainConnection = false;
4503 }
4504
4505 // Get flags.
4506
4507 context->flags = GetDNSSDFlagsFromOpts();
4508 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4509
4510 // Get interface index.
4511
4512 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4513 require_noerr_quiet( err, exit );
4514
4515 // Create reverse lookup record name.
4516
4517 err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
4518 &ipv4Addr, NULL, NULL, NULL, &endPtr );
4519 if( err || ( *endPtr != '\0' ) )
4520 {
4521 char * dst;
4522 int i;
4523
4524 err = _StringToIPv6Address( gReverseLookup_IPAddr,
4525 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
4526 ipv6Addr, NULL, NULL, NULL, &endPtr );
4527 if( err || ( *endPtr != '\0' ) )
4528 {
4529 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
4530 err = kParamErr;
4531 goto exit;
4532 }
4533 dst = recordName;
4534 for( i = 15; i >= 0; --i )
4535 {
4536 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
4537 *dst++ = '.';
4538 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
4539 *dst++ = '.';
4540 }
4541 strcpy_literal( dst, kIP6ARPADomainStr );
4542 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
4543 }
4544 else
4545 {
4546 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
4547 ipv4Addr & 0xFF,
4548 ( ipv4Addr >> 8 ) & 0xFF,
4549 ( ipv4Addr >> 16 ) & 0xFF,
4550 ( ipv4Addr >> 24 ) & 0xFF );
4551 }
4552
4553 // Set remaining parameters.
4554
4555 context->recordName = recordName;
4556 context->recordType = kDNSServiceType_PTR;
4557 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
4558 context->oneShotMode = gReverseLookup_OneShot ? true : false;
4559
4560 // Print prologue.
4561
4562 QueryRecordPrintPrologue( context );
4563
4564 // Start operation.
4565
4566 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4567 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
4568 kDNSServiceClass_IN, QueryRecordCallback, context );
4569 require_noerr( err, exit );
4570
4571 context->opRef = sdRef;
4572 if( !useMainConnection )
4573 {
4574 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4575 require_noerr( err, exit );
4576 }
4577
4578 // Set time limit.
4579
4580 if( context->timeLimitSecs > 0 )
4581 {
4582 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4583 kExitReason_TimeLimit, Exit );
4584 }
4585 dispatch_main();
4586
4587 exit:
4588 dispatch_source_forget( &signalSource );
4589 if( context ) QueryRecordContextFree( context );
4590 if( err ) exit( 1 );
4591 }
4592
4593 //===========================================================================================================================
4594 // PortMappingCmd
4595 //===========================================================================================================================
4596
4597 typedef struct
4598 {
4599 DNSServiceRef mainRef; // Main sdRef for shared connection.
4600 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
4601 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
4602 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4603 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4604 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
4605 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4606 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
4607 Boolean printedHeader; // True if results header was printed.
4608
4609 } PortMappingContext;
4610
4611 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
4612 static void PortMappingContextFree( PortMappingContext *inContext );
4613 static void DNSSD_API
4614 PortMappingCallback(
4615 DNSServiceRef inSDRef,
4616 DNSServiceFlags inFlags,
4617 uint32_t inInterfaceIndex,
4618 DNSServiceErrorType inError,
4619 uint32_t inExternalIPv4Address,
4620 DNSServiceProtocol inProtocol,
4621 uint16_t inInternalPort,
4622 uint16_t inExternalPort,
4623 uint32_t inTTL,
4624 void * inContext );
4625
4626 static void PortMappingCmd( void )
4627 {
4628 OSStatus err;
4629 PortMappingContext * context = NULL;
4630 DNSServiceRef sdRef;
4631 dispatch_source_t signalSource = NULL;
4632 int useMainConnection;
4633
4634 // Set up SIGINT handler.
4635
4636 signal( SIGINT, SIG_IGN );
4637 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4638 require_noerr( err, exit );
4639 dispatch_resume( signalSource );
4640
4641 // Create context.
4642
4643 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
4644 require_action( context, exit, err = kNoMemoryErr );
4645
4646 // Check command parameters.
4647
4648 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
4649 {
4650 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
4651 err = kParamErr;
4652 goto exit;
4653 }
4654
4655 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
4656 {
4657 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
4658 err = kParamErr;
4659 goto exit;
4660 }
4661
4662 // Create main connection.
4663
4664 if( gConnectionOpt )
4665 {
4666 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4667 require_noerr_quiet( err, exit );
4668 useMainConnection = true;
4669 }
4670 else
4671 {
4672 useMainConnection = false;
4673 }
4674
4675 // Get flags.
4676
4677 context->flags = GetDNSSDFlagsFromOpts();
4678 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4679
4680 // Get interface index.
4681
4682 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4683 require_noerr_quiet( err, exit );
4684
4685 // Set remaining parameters.
4686
4687 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
4688 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
4689 context->ttl = (uint32_t) gPortMapping_TTL;
4690 context->internalPort = (uint16_t) gPortMapping_InternalPort;
4691 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
4692
4693 // Print prologue.
4694
4695 PortMappingPrintPrologue( context );
4696
4697 // Start operation.
4698
4699 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4700 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
4701 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
4702 require_noerr( err, exit );
4703
4704 context->opRef = sdRef;
4705 if( !useMainConnection )
4706 {
4707 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4708 require_noerr( err, exit );
4709 }
4710
4711 dispatch_main();
4712
4713 exit:
4714 dispatch_source_forget( &signalSource );
4715 if( context ) PortMappingContextFree( context );
4716 if( err ) exit( 1 );
4717 }
4718
4719 //===========================================================================================================================
4720 // PortMappingPrintPrologue
4721 //===========================================================================================================================
4722
4723 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
4724 {
4725 char ifName[ kInterfaceNameBufLen ];
4726
4727 InterfaceIndexToName( inContext->ifIndex, ifName );
4728
4729 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4730 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4731 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
4732 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
4733 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
4734 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
4735 " (system will use a default value.)" );
4736 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4737 FPrintF( stdout, "---\n" );
4738
4739 }
4740
4741 //===========================================================================================================================
4742 // PortMappingContextFree
4743 //===========================================================================================================================
4744
4745 static void PortMappingContextFree( PortMappingContext *inContext )
4746 {
4747 DNSServiceForget( &inContext->opRef );
4748 DNSServiceForget( &inContext->mainRef );
4749 free( inContext );
4750 }
4751
4752 //===========================================================================================================================
4753 // PortMappingCallback
4754 //===========================================================================================================================
4755
4756 static void DNSSD_API
4757 PortMappingCallback(
4758 DNSServiceRef inSDRef,
4759 DNSServiceFlags inFlags,
4760 uint32_t inInterfaceIndex,
4761 DNSServiceErrorType inError,
4762 uint32_t inExternalIPv4Address,
4763 DNSServiceProtocol inProtocol,
4764 uint16_t inInternalPort,
4765 uint16_t inExternalPort,
4766 uint32_t inTTL,
4767 void * inContext )
4768 {
4769 PortMappingContext * const context = (PortMappingContext *) inContext;
4770 struct timeval now;
4771 char errorStr[ 128 ];
4772
4773 Unused( inSDRef );
4774 Unused( inFlags );
4775
4776 gettimeofday( &now, NULL );
4777
4778 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
4779 if( !context->printedHeader )
4780 {
4781 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4782 context->printedHeader = true;
4783 }
4784 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4785 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
4786 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
4787 }
4788
4789 //===========================================================================================================================
4790 // BrowseAllCmd
4791 //===========================================================================================================================
4792
4793 typedef struct BrowseAllConnection BrowseAllConnection;
4794
4795 typedef struct
4796 {
4797 ServiceBrowserRef browser; // Service browser.
4798 ServiceBrowserResults * results; // Results from the service browser.
4799 BrowseAllConnection * connectionList; // List of connections.
4800 dispatch_source_t connectionTimer; // Timer for connection timeout.
4801 int connectionPendingCount; // Number of pending connections.
4802 int connectionTimeoutSecs; // Timeout value for connections in seconds.
4803
4804 } BrowseAllContext;
4805
4806 struct BrowseAllConnection
4807 {
4808 BrowseAllConnection * next; // Next connection object in list.
4809 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
4810 uint16_t port; // TCP port to connect to.
4811 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
4812 OSStatus status; // Status of connection. NoErr means connection succeeded.
4813 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
4814 int32_t refCount; // This object's reference count.
4815 BrowseAllContext * context; // Back pointer to parent context.
4816 };
4817
4818 static void _BrowseAllContextFree( BrowseAllContext *inContext );
4819 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
4820 static OSStatus
4821 _BrowseAllConnectionCreate(
4822 const struct sockaddr * inSockAddr,
4823 uint16_t inPort,
4824 BrowseAllContext * inContext,
4825 BrowseAllConnection ** outConnection );
4826 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
4827 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
4828 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
4829 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
4830 static void _BrowseAllExit( void *inContext );
4831
4832 static Boolean _IsServiceTypeTCP( const char *inServiceType );
4833
4834 static void BrowseAllCmd( void )
4835 {
4836 OSStatus err;
4837 BrowseAllContext * context = NULL;
4838 size_t i;
4839 uint32_t ifIndex;
4840 char ifName[ kInterfaceNameBufLen ];
4841
4842 // Check parameters.
4843
4844 if( gBrowseAll_BrowseTimeSecs <= 0 )
4845 {
4846 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
4847 err = kParamErr;
4848 goto exit;
4849 }
4850
4851 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
4852 require_action( context, exit, err = kNoMemoryErr );
4853
4854 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
4855 #if( TARGET_OS_POSIX )
4856 // Increase the open file descriptor limit for connection sockets.
4857
4858 if( context->connectionTimeoutSecs > 0 )
4859 {
4860 struct rlimit fdLimits;
4861
4862 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
4863 err = map_global_noerr_errno( err );
4864 require_noerr( err, exit );
4865
4866 if( fdLimits.rlim_cur < 4096 )
4867 {
4868 fdLimits.rlim_cur = 4096;
4869 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
4870 err = map_global_noerr_errno( err );
4871 require_noerr( err, exit );
4872 }
4873 }
4874 #endif
4875
4876 // Get interface index.
4877
4878 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4879 require_noerr_quiet( err, exit );
4880
4881 // Print prologue.
4882
4883 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4884 FPrintF( stdout, "Service types: ");
4885 if( gBrowseAll_ServiceTypesCount > 0 )
4886 {
4887 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
4888 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
4889 {
4890 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
4891 }
4892 FPrintF( stdout, "\n" );
4893 }
4894 else
4895 {
4896 FPrintF( stdout, "all services\n" );
4897 }
4898 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
4899 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
4900 FPrintF( stdout, "Connect timeout: %d second%?c\n",
4901 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
4902 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
4903 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4904 FPrintF( stdout, "---\n" );
4905
4906 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
4907 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
4908 require_noerr( err, exit );
4909
4910 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
4911 {
4912 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
4913 require_noerr( err, exit );
4914 }
4915 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
4916 ServiceBrowserStart( context->browser );
4917 dispatch_main();
4918
4919 exit:
4920 if( context ) _BrowseAllContextFree( context );
4921 if( err ) exit( 1 );
4922 }
4923
4924 //===========================================================================================================================
4925 // _BrowseAllContextFree
4926 //===========================================================================================================================
4927
4928 static void _BrowseAllContextFree( BrowseAllContext *inContext )
4929 {
4930 check( !inContext->browser );
4931 check( !inContext->connectionTimer );
4932 check( !inContext->connectionList );
4933 ForgetServiceBrowserResults( &inContext->results );
4934 free( inContext );
4935 }
4936
4937 //===========================================================================================================================
4938 // _BrowseAllServiceBrowserCallback
4939 //===========================================================================================================================
4940
4941 #define kDiscardProtocolPort 9
4942
4943 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
4944 {
4945 OSStatus err;
4946 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4947 SBRDomain * domain;
4948 SBRServiceType * type;
4949 SBRServiceInstance * instance;
4950 SBRIPAddress * ipaddr;
4951
4952 Unused( inError );
4953
4954 require_action( inResults, exit, err = kUnexpectedErr );
4955
4956 check( !context->results );
4957 context->results = inResults;
4958 ServiceBrowserResultsRetain( context->results );
4959
4960 check( context->connectionPendingCount == 0 );
4961 if( context->connectionTimeoutSecs > 0 )
4962 {
4963 BrowseAllConnection * connection;
4964 BrowseAllConnection ** connectionPtr = &context->connectionList;
4965 char destination[ kSockAddrStringMaxSize ];
4966
4967 for( domain = context->results->domainList; domain; domain = domain->next )
4968 {
4969 for( type = domain->typeList; type; type = type->next )
4970 {
4971 if( !_IsServiceTypeTCP( type->name ) ) continue;
4972 for( instance = type->instanceList; instance; instance = instance->next )
4973 {
4974 if( instance->port == kDiscardProtocolPort ) continue;
4975 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
4976 {
4977 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
4978 require_noerr( err, exit );
4979
4980 *connectionPtr = connection;
4981 connectionPtr = &connection->next;
4982
4983 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
4984 check_noerr( err );
4985 if( !err )
4986 {
4987 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
4988 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
4989 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
4990 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
4991 dispatch_get_main_queue() );
4992 check_noerr( err );
4993 }
4994 if( !err )
4995 {
4996 _BrowseAllConnectionRetain( connection );
4997 connection->status = kInProgressErr;
4998 ++context->connectionPendingCount;
4999 }
5000 else
5001 {
5002 connection->status = err;
5003 }
5004 }
5005 }
5006 }
5007 }
5008 }
5009
5010 if( context->connectionPendingCount > 0 )
5011 {
5012 check( !context->connectionTimer );
5013 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5014 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5015 require_noerr( err, exit );
5016 dispatch_resume( context->connectionTimer );
5017 }
5018 else
5019 {
5020 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5021 }
5022 err = kNoErr;
5023
5024 exit:
5025 ForgetCF( &context->browser );
5026 if( err ) exit( 1 );
5027 }
5028
5029 //===========================================================================================================================
5030 // _BrowseAllConnectionCreate
5031 //===========================================================================================================================
5032
5033 static OSStatus
5034 _BrowseAllConnectionCreate(
5035 const struct sockaddr * inSockAddr,
5036 uint16_t inPort,
5037 BrowseAllContext * inContext,
5038 BrowseAllConnection ** outConnection )
5039 {
5040 OSStatus err;
5041 BrowseAllConnection * obj;
5042
5043 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5044 require_action( obj, exit, err = kNoMemoryErr );
5045
5046 obj->refCount = 1;
5047 SockAddrCopy( inSockAddr, &obj->sip );
5048 obj->port = inPort;
5049 obj->context = inContext;
5050
5051 *outConnection = obj;
5052 err = kNoErr;
5053
5054 exit:
5055 return( err );
5056 }
5057
5058 //===========================================================================================================================
5059 // _BrowseAllConnectionRetain
5060 //===========================================================================================================================
5061
5062 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5063 {
5064 ++inConnection->refCount;
5065 }
5066
5067 //===========================================================================================================================
5068 // _BrowseAllConnectionRelease
5069 //===========================================================================================================================
5070
5071 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5072 {
5073 if( --inConnection->refCount == 0 ) free( inConnection );
5074 }
5075
5076 //===========================================================================================================================
5077 // _BrowseAllConnectionProgress
5078 //===========================================================================================================================
5079
5080 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5081 {
5082 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5083
5084 if( inPhase == kAsyncConnectionPhase_Connected )
5085 {
5086 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5087
5088 connection->connectTimeSecs = info->connectSecs;
5089 }
5090 }
5091
5092 //===========================================================================================================================
5093 // _BrowseAllConnectionHandler
5094 //===========================================================================================================================
5095
5096 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5097 {
5098 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5099 BrowseAllContext * const context = connection->context;
5100
5101 connection->status = inError;
5102 ForgetSocket( &inSock );
5103 if( context )
5104 {
5105 check( context->connectionPendingCount > 0 );
5106 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5107 {
5108 dispatch_source_forget( &context->connectionTimer );
5109 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5110 }
5111 }
5112 _BrowseAllConnectionRelease( connection );
5113 }
5114
5115 //===========================================================================================================================
5116 // _BrowseAllExit
5117 //===========================================================================================================================
5118
5119 #define Indent( X ) ( (X) * 4 ), ""
5120
5121 static void _BrowseAllExit( void *inContext )
5122 {
5123 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5124 SBRDomain * domain;
5125 SBRServiceType * type;
5126 SBRServiceInstance * instance;
5127 SBRIPAddress * ipaddr;
5128 char textBuf[ 512 ];
5129 #if( TARGET_OS_POSIX )
5130 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5131 #endif
5132
5133 dispatch_source_forget( &context->connectionTimer );
5134
5135 for( domain = context->results->domainList; domain; domain = domain->next )
5136 {
5137 FPrintF( stdout, "%s\n\n", domain->name );
5138
5139 for( type = domain->typeList; type; type = type->next )
5140 {
5141 const char * description;
5142 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5143
5144 description = ServiceTypeDescription( type->name );
5145 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5146 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5147
5148 for( instance = type->instanceList; instance; instance = instance->next )
5149 {
5150 char * dst = textBuf;
5151 char * const lim = &textBuf[ countof( textBuf ) ];
5152 char ifname[ IF_NAMESIZE + 1 ];
5153
5154 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5155 if( instance->ifIndex == 0 )
5156 {
5157 SNPrintF_Add( &dst, lim, "the Internet" );
5158 }
5159 else if( if_indextoname( instance->ifIndex, ifname ) )
5160 {
5161 NetTransportType netType;
5162
5163 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5164 SNPrintF_Add( &dst, lim, "%s (%s)",
5165 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5166 ifname );
5167 }
5168 else
5169 {
5170 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5171 }
5172 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5173 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5174
5175 if( instance->hostname )
5176 {
5177 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5178 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5179 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5180 }
5181 else
5182 {
5183 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5184 }
5185
5186 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5187 {
5188 BrowseAllConnection * conn;
5189 BrowseAllConnection ** connPtr;
5190
5191 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5192 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5193
5194 conn = NULL;
5195 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5196 {
5197 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5198 {
5199 if( ( conn->port == instance->port ) &&
5200 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5201 }
5202 if( conn )
5203 {
5204 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5205 *connPtr = conn->next;
5206 conn->context = NULL;
5207 AsyncConnection_Forget( &conn->asyncCnx );
5208 }
5209 }
5210
5211 if( conn )
5212 {
5213 if( conn->status == kNoErr )
5214 {
5215 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5216 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5217 }
5218 else
5219 {
5220 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5221 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5222 }
5223 _BrowseAllConnectionRelease( conn );
5224 }
5225 else
5226 {
5227 FPrintF( stdout, " (no connection attempted)\n" );
5228 }
5229 }
5230
5231 FPrintF( stdout, "\n" );
5232 if( instance->txtLen == 0 ) continue;
5233
5234 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5235 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5236 if( instance->txtLen > 1 )
5237 {
5238 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5239 }
5240 else
5241 {
5242 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5243 }
5244 FPrintF( stdout, "\n" );
5245 }
5246 FPrintF( stdout, "\n" );
5247 }
5248 }
5249
5250 _BrowseAllContextFree( context );
5251 Exit( NULL );
5252 }
5253
5254 //===========================================================================================================================
5255 // _IsServiceTypeTCP
5256 //===========================================================================================================================
5257
5258 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5259 {
5260 OSStatus err;
5261 const uint8_t * secondLabel;
5262 uint8_t name[ kDomainNameLengthMax ];
5263
5264 err = DomainNameFromString( name, inServiceType, NULL );
5265 if( !err )
5266 {
5267 secondLabel = DomainNameGetNextLabel( name );
5268 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5269 }
5270 return( false );
5271 }
5272
5273 //===========================================================================================================================
5274 // GetNameInfoCmd
5275 //===========================================================================================================================
5276
5277 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5278 {
5279 CaseFlagStringify( NI_NUMERICSCOPE ),
5280 CaseFlagStringify( NI_DGRAM ),
5281 CaseFlagStringify( NI_NUMERICSERV ),
5282 CaseFlagStringify( NI_NAMEREQD ),
5283 CaseFlagStringify( NI_NUMERICHOST ),
5284 CaseFlagStringify( NI_NOFQDN ),
5285 { 0, NULL }
5286 };
5287
5288 static void GetNameInfoCmd( void )
5289 {
5290 OSStatus err;
5291 sockaddr_ip sip;
5292 size_t sockAddrLen;
5293 unsigned int flags;
5294 const FlagStringPair * pair;
5295 struct timeval now;
5296 char host[ NI_MAXHOST ];
5297 char serv[ NI_MAXSERV ];
5298
5299 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5300 check_noerr( err );
5301 if( err )
5302 {
5303 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5304 goto exit;
5305 }
5306
5307 flags = 0;
5308 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5309 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5310 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5311 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5312 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5313 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5314
5315 // Print prologue.
5316
5317 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5318 FPrintF( stdout, "Flags: 0x%X < ", flags );
5319 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5320 {
5321 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5322 }
5323 FPrintF( stdout, ">\n" );
5324 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5325 FPrintF( stdout, "---\n" );
5326
5327 // Call getnameinfo().
5328
5329 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5330 (int) flags );
5331 gettimeofday( &now, NULL );
5332 if( err )
5333 {
5334 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5335 }
5336 else
5337 {
5338 FPrintF( stdout, "host: %s\n", host );
5339 FPrintF( stdout, "serv: %s\n", serv );
5340 }
5341 FPrintF( stdout, "---\n" );
5342 FPrintF( stdout, "End time: %{du:time}\n", &now );
5343
5344 exit:
5345 gExitCode = err ? 1 : 0;
5346 }
5347
5348 //===========================================================================================================================
5349 // GetAddrInfoStressCmd
5350 //===========================================================================================================================
5351
5352 typedef struct
5353 {
5354 DNSServiceRef mainRef;
5355 DNSServiceRef sdRef;
5356 DNSServiceFlags flags;
5357 unsigned int interfaceIndex;
5358 unsigned int connectionNumber;
5359 unsigned int requestCount;
5360 unsigned int requestCountMax;
5361 unsigned int requestCountLimit;
5362 unsigned int durationMinMs;
5363 unsigned int durationMaxMs;
5364
5365 } GAIStressContext;
5366
5367 static void GetAddrInfoStressEvent( void *inContext );
5368 static void DNSSD_API
5369 GetAddrInfoStressCallback(
5370 DNSServiceRef inSDRef,
5371 DNSServiceFlags inFlags,
5372 uint32_t inInterfaceIndex,
5373 DNSServiceErrorType inError,
5374 const char * inHostname,
5375 const struct sockaddr * inSockAddr,
5376 uint32_t inTTL,
5377 void * inContext );
5378
5379 static void GetAddrInfoStressCmd( void )
5380 {
5381 OSStatus err;
5382 GAIStressContext * context = NULL;
5383 int i;
5384 DNSServiceFlags flags;
5385 uint32_t ifIndex;
5386 char ifName[ kInterfaceNameBufLen ];
5387
5388 if( gGAIStress_TestDurationSecs < 0 )
5389 {
5390 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5391 err = kParamErr;
5392 goto exit;
5393 }
5394 if( gGAIStress_ConnectionCount <= 0 )
5395 {
5396 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5397 err = kParamErr;
5398 goto exit;
5399 }
5400 if( gGAIStress_DurationMinMs <= 0 )
5401 {
5402 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5403 err = kParamErr;
5404 goto exit;
5405 }
5406 if( gGAIStress_DurationMaxMs <= 0 )
5407 {
5408 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5409 err = kParamErr;
5410 goto exit;
5411 }
5412 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5413 {
5414 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5415 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5416 err = kParamErr;
5417 goto exit;
5418 }
5419 if( gGAIStress_RequestCountMax <= 0 )
5420 {
5421 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5422 err = kParamErr;
5423 goto exit;
5424 }
5425
5426 // Set flags.
5427
5428 flags = GetDNSSDFlagsFromOpts();
5429
5430 // Set interface index.
5431
5432 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5433 require_noerr_quiet( err, exit );
5434
5435 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5436 {
5437 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5438 require_action( context, exit, err = kNoMemoryErr );
5439
5440 context->flags = flags;
5441 context->interfaceIndex = ifIndex;
5442 context->connectionNumber = (unsigned int)( i + 1 );
5443 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5444 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5445 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5446
5447 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5448 context = NULL;
5449 }
5450
5451 if( gGAIStress_TestDurationSecs > 0 )
5452 {
5453 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5454 }
5455
5456 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5457 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5458 FPrintF( stdout, "Test duration: " );
5459 if( gGAIStress_TestDurationSecs == 0 )
5460 {
5461 FPrintF( stdout, "∞\n" );
5462 }
5463 else
5464 {
5465 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5466 }
5467 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5468 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5469 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5470 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5471 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5472 FPrintF( stdout, "---\n" );
5473
5474 dispatch_main();
5475
5476 exit:
5477 FreeNullSafe( context );
5478 if( err ) exit( 1 );
5479 }
5480
5481 //===========================================================================================================================
5482 // GetAddrInfoStressEvent
5483 //===========================================================================================================================
5484
5485 #define kStressRandStrLen 5
5486
5487 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5488
5489 static void GetAddrInfoStressEvent( void *inContext )
5490 {
5491 GAIStressContext * const context = (GAIStressContext *) inContext;
5492 OSStatus err;
5493 DNSServiceRef sdRef;
5494 unsigned int nextMs;
5495 char randomStr[ kStressRandStrLen + 1 ];
5496 char hostname[ kStressRandStrLen + 4 + 1 ];
5497 Boolean isConnectionNew = false;
5498 static Boolean printedHeader = false;
5499
5500 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5501 {
5502 DNSServiceForget( &context->mainRef );
5503 context->sdRef = NULL;
5504 context->requestCount = 0;
5505 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5506
5507 err = DNSServiceCreateConnection( &context->mainRef );
5508 require_noerr( err, exit );
5509
5510 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5511 require_noerr( err, exit );
5512
5513 isConnectionNew = true;
5514 }
5515
5516 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5517 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5518
5519 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5520
5521 if( !printedHeader )
5522 {
5523 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5524 printedHeader = true;
5525 }
5526 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5527 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5528
5529 DNSServiceForget( &context->sdRef );
5530 sdRef = context->mainRef;
5531 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5532 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5533 require_noerr( err, exit );
5534 context->sdRef = sdRef;
5535
5536 context->requestCount++;
5537
5538 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5539
5540 exit:
5541 if( err ) exit( 1 );
5542 }
5543
5544 //===========================================================================================================================
5545 // GetAddrInfoStressCallback
5546 //===========================================================================================================================
5547
5548 static void DNSSD_API
5549 GetAddrInfoStressCallback(
5550 DNSServiceRef inSDRef,
5551 DNSServiceFlags inFlags,
5552 uint32_t inInterfaceIndex,
5553 DNSServiceErrorType inError,
5554 const char * inHostname,
5555 const struct sockaddr * inSockAddr,
5556 uint32_t inTTL,
5557 void * inContext )
5558 {
5559 Unused( inSDRef );
5560 Unused( inFlags );
5561 Unused( inInterfaceIndex );
5562 Unused( inError );
5563 Unused( inHostname );
5564 Unused( inSockAddr );
5565 Unused( inTTL );
5566 Unused( inContext );
5567 }
5568
5569 //===========================================================================================================================
5570 // DNSQueryCmd
5571 //===========================================================================================================================
5572
5573 typedef struct
5574 {
5575 sockaddr_ip serverAddr;
5576 uint64_t sendTicks;
5577 uint8_t * msgPtr;
5578 size_t msgLen;
5579 size_t msgOffset;
5580 const char * name;
5581 dispatch_source_t readSource;
5582 SocketRef sock;
5583 int timeLimitSecs;
5584 uint16_t queryID;
5585 uint16_t type;
5586 Boolean haveTCPLen;
5587 Boolean useTCP;
5588 Boolean printRawRData; // True if RDATA results are not to be formatted.
5589 uint8_t msgBuf[ 512 ];
5590
5591 } DNSQueryContext;
5592
5593 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5594 static void DNSQueryReadHandler( void *inContext );
5595 static void DNSQueryCancelHandler( void *inContext );
5596
5597 static void DNSQueryCmd( void )
5598 {
5599 OSStatus err;
5600 DNSQueryContext * context = NULL;
5601 uint8_t * msgPtr;
5602 size_t msgLen, sendLen;
5603
5604 // Check command parameters.
5605
5606 if( gDNSQuery_TimeLimitSecs < -1 )
5607 {
5608 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5609 err = kParamErr;
5610 goto exit;
5611 }
5612 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5613 {
5614 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5615 err = kParamErr;
5616 goto exit;
5617 }
5618
5619 // Create context.
5620
5621 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5622 require_action( context, exit, err = kNoMemoryErr );
5623
5624 context->name = gDNSQuery_Name;
5625 context->sock = kInvalidSocketRef;
5626 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5627 context->queryID = (uint16_t) Random32();
5628 context->useTCP = gDNSQuery_UseTCP ? true : false;
5629 context->printRawRData = gDNSQuery_RawRData ? true : false;
5630
5631 #if( TARGET_OS_DARWIN )
5632 if( gDNSQuery_Server )
5633 #endif
5634 {
5635 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5636 require_noerr( err, exit );
5637 }
5638 #if( TARGET_OS_DARWIN )
5639 else
5640 {
5641 err = GetDefaultDNSServer( &context->serverAddr );
5642 require_noerr( err, exit );
5643 }
5644 #endif
5645 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5646
5647 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5648 require_noerr( err, exit );
5649
5650 // Write query message.
5651
5652 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5653
5654 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5655 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5656 kDNSServiceClass_IN, &msgLen );
5657 require_noerr( err, exit );
5658 check( msgLen <= UINT16_MAX );
5659
5660 if( context->useTCP )
5661 {
5662 WriteBig16( context->msgBuf, msgLen );
5663 sendLen = 2 + msgLen;
5664 }
5665 else
5666 {
5667 sendLen = msgLen;
5668 }
5669
5670 DNSQueryPrintPrologue( context );
5671
5672 if( gDNSQuery_Verbose )
5673 {
5674 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5675 FPrintF( stdout, "---\n" );
5676 }
5677
5678 if( context->useTCP )
5679 {
5680 // Create TCP socket.
5681
5682 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5683 err = map_socket_creation_errno( context->sock );
5684 require_noerr( err, exit );
5685
5686 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5687 require_noerr( err, exit );
5688 }
5689 else
5690 {
5691 // Create UDP socket.
5692
5693 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5694 require_noerr( err, exit );
5695 }
5696
5697 context->sendTicks = UpTicks();
5698 err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5699 require_noerr( err, exit );
5700
5701 if( context->timeLimitSecs == 0 ) goto exit;
5702
5703 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5704 &context->readSource );
5705 require_noerr( err, exit );
5706 dispatch_resume( context->readSource );
5707
5708 if( context->timeLimitSecs > 0 )
5709 {
5710 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5711 Exit );
5712 }
5713 dispatch_main();
5714
5715 exit:
5716 if( context )
5717 {
5718 dispatch_source_forget( &context->readSource );
5719 ForgetSocket( &context->sock );
5720 free( context );
5721 }
5722 if( err ) exit( 1 );
5723 }
5724
5725 //===========================================================================================================================
5726 // DNSQueryPrintPrologue
5727 //===========================================================================================================================
5728
5729 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5730 {
5731 const int timeLimitSecs = inContext->timeLimitSecs;
5732
5733 FPrintF( stdout, "Name: %s\n", inContext->name );
5734 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5735 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5736 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5737 FPrintF( stdout, "Time limit: " );
5738 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5739 else FPrintF( stdout, "∞\n" );
5740 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5741 FPrintF( stdout, "---\n" );
5742 }
5743
5744 //===========================================================================================================================
5745 // DNSQueryReadHandler
5746 //===========================================================================================================================
5747
5748 static void DNSQueryReadHandler( void *inContext )
5749 {
5750 OSStatus err;
5751 struct timeval now;
5752 const uint64_t nowTicks = UpTicks();
5753 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5754
5755 gettimeofday( &now, NULL );
5756
5757 if( context->useTCP )
5758 {
5759 if( !context->haveTCPLen )
5760 {
5761 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5762 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5763 require_noerr( err, exit );
5764
5765 context->msgOffset = 0;
5766 context->msgLen = ReadBig16( context->msgBuf );
5767 context->haveTCPLen = true;
5768 if( context->msgLen <= sizeof( context->msgBuf ) )
5769 {
5770 context->msgPtr = context->msgBuf;
5771 }
5772 else
5773 {
5774 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5775 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5776 }
5777 }
5778
5779 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5780 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5781 require_noerr( err, exit );
5782 context->msgOffset = 0;
5783 context->haveTCPLen = false;
5784 }
5785 else
5786 {
5787 sockaddr_ip fromAddr;
5788
5789 context->msgPtr = context->msgBuf;
5790 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5791 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5792 require_noerr( err, exit );
5793
5794 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5795 }
5796
5797 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
5798 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5799 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5800 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5801 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
5802
5803 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5804 {
5805 Exit( kExitReason_ReceivedResponse );
5806 }
5807
5808 exit:
5809 if( err ) dispatch_source_forget( &context->readSource );
5810 }
5811
5812 //===========================================================================================================================
5813 // DNSQueryCancelHandler
5814 //===========================================================================================================================
5815
5816 static void DNSQueryCancelHandler( void *inContext )
5817 {
5818 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5819
5820 check( !context->readSource );
5821 ForgetSocket( &context->sock );
5822 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5823 free( context );
5824 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5825 }
5826
5827 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5828 //===========================================================================================================================
5829 // DNSCryptCmd
5830 //===========================================================================================================================
5831
5832 #define kDNSCryptPort 443
5833
5834 #define kDNSCryptMinPadLength 8
5835 #define kDNSCryptMaxPadLength 256
5836 #define kDNSCryptBlockSize 64
5837 #define kDNSCryptCertMinimumLength 124
5838 #define kDNSCryptClientMagicLength 8
5839 #define kDNSCryptResolverMagicLength 8
5840 #define kDNSCryptHalfNonceLength 12
5841 #define kDNSCryptCertMagicLength 4
5842
5843 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5844
5845 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5846 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5847 {
5848 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5849 };
5850
5851 typedef struct
5852 {
5853 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5854 uint8_t esVersion[ 2 ];
5855 uint8_t minorVersion[ 2 ];
5856 uint8_t signature[ crypto_sign_BYTES ];
5857 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5858 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5859 uint8_t serial[ 4 ];
5860 uint8_t startTime[ 4 ];
5861 uint8_t endTime[ 4 ];
5862 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5863
5864 } DNSCryptCert;
5865
5866 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5867
5868 typedef struct
5869 {
5870 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5871 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5872 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5873 uint8_t poly1305MAC[ 16 ];
5874
5875 } DNSCryptQueryHeader;
5876
5877 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5878 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5879 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5880 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5881
5882 typedef struct
5883 {
5884 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5885 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5886 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5887 uint8_t poly1305MAC[ 16 ];
5888
5889 } DNSCryptResponseHeader;
5890
5891 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5892 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5893 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5894 sizeof( DNSCryptResponseHeader ) );
5895
5896 typedef struct
5897 {
5898 sockaddr_ip serverAddr;
5899 uint64_t sendTicks;
5900 const char * providerName;
5901 const char * qname;
5902 const uint8_t * certPtr;
5903 size_t certLen;
5904 dispatch_source_t readSource;
5905 size_t msgLen;
5906 int timeLimitSecs;
5907 uint16_t queryID;
5908 uint16_t qtype;
5909 Boolean printRawRData;
5910 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5911 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5912 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5913 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5914 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5915 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5916 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5917 uint8_t msgBuf[ 512 ];
5918
5919 } DNSCryptContext;
5920
5921 static void DNSCryptReceiveCertHandler( void *inContext );
5922 static void DNSCryptReceiveResponseHandler( void *inContext );
5923 static void DNSCryptProceed( void *inContext );
5924 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5925 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5926 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5927 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5928
5929 static void DNSCryptCmd( void )
5930 {
5931 OSStatus err;
5932 DNSCryptContext * context = NULL;
5933 size_t writtenBytes;
5934 size_t totalBytes;
5935 SocketContext * sockCtx;
5936 SocketRef sock = kInvalidSocketRef;
5937 const char * ptr;
5938
5939 // Check command parameters.
5940
5941 if( gDNSCrypt_TimeLimitSecs < -1 )
5942 {
5943 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5944 err = kParamErr;
5945 goto exit;
5946 }
5947
5948 // Create context.
5949
5950 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5951 require_action( context, exit, err = kNoMemoryErr );
5952
5953 context->providerName = gDNSCrypt_ProviderName;
5954 context->qname = gDNSCrypt_Name;
5955 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5956 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5957
5958 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5959 require_noerr( err, exit );
5960
5961 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5962 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5963 if( err || ( *ptr != '\0' ) )
5964 {
5965 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5966 goto exit;
5967 }
5968 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5969 {
5970 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5971 totalBytes, sizeof( context->serverPublicSignKey ) );
5972 err = kSizeErr;
5973 goto exit;
5974 }
5975 check( writtenBytes == totalBytes );
5976
5977 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5978 require_noerr( err, exit );
5979 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
5980
5981 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
5982 require_noerr( err, exit );
5983
5984 // Write query message.
5985
5986 context->queryID = (uint16_t) Random32();
5987 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
5988 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
5989 require_noerr( err, exit );
5990
5991 // Create UDP socket.
5992
5993 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
5994 require_noerr( err, exit );
5995
5996 // Send DNS query.
5997
5998 context->sendTicks = UpTicks();
5999 err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6000 require_noerr( err, exit );
6001
6002 err = SocketContextCreate( sock, context, &sockCtx );
6003 require_noerr( err, exit );
6004 sock = kInvalidSocketRef;
6005
6006 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6007 &context->readSource );
6008 if( err ) ForgetSocketContext( &sockCtx );
6009 require_noerr( err, exit );
6010
6011 dispatch_resume( context->readSource );
6012
6013 if( context->timeLimitSecs > 0 )
6014 {
6015 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6016 Exit );
6017 }
6018 dispatch_main();
6019
6020 exit:
6021 if( context ) free( context );
6022 ForgetSocket( &sock );
6023 if( err ) exit( 1 );
6024 }
6025
6026 //===========================================================================================================================
6027 // DNSCryptReceiveCertHandler
6028 //===========================================================================================================================
6029
6030 static void DNSCryptReceiveCertHandler( void *inContext )
6031 {
6032 OSStatus err;
6033 struct timeval now;
6034 const uint64_t nowTicks = UpTicks();
6035 SocketContext * const sockCtx = (SocketContext *) inContext;
6036 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6037 const DNSHeader * hdr;
6038 sockaddr_ip fromAddr;
6039 const uint8_t * ptr;
6040 const uint8_t * txtPtr;
6041 size_t txtLen;
6042 unsigned int answerCount, i;
6043 uint8_t targetName[ kDomainNameLengthMax ];
6044
6045 gettimeofday( &now, NULL );
6046
6047 dispatch_source_forget( &context->readSource );
6048
6049 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6050 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6051 require_noerr( err, exit );
6052 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6053
6054 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6055 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6056 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6057 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6058 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6059
6060 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6061
6062 hdr = (DNSHeader *) context->msgBuf;
6063 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6064
6065 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6066 require_noerr( err, exit );
6067
6068 err = DomainNameFromString( targetName, context->providerName, NULL );
6069 require_noerr( err, exit );
6070
6071 answerCount = DNSHeaderGetAnswerCount( hdr );
6072 for( i = 0; i < answerCount; ++i )
6073 {
6074 uint16_t type;
6075 uint16_t class;
6076 uint8_t name[ kDomainNameLengthMax ];
6077
6078 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6079 &ptr );
6080 require_noerr( err, exit );
6081
6082 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6083 {
6084 break;
6085 }
6086 }
6087
6088 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6089 {
6090 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6091 err = kSizeErr;
6092 goto exit;
6093 }
6094 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6095 {
6096 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6097 err = kSizeErr;
6098 goto exit;
6099 }
6100
6101 context->certLen = txtPtr[ 0 ];
6102 context->certPtr = &txtPtr[ 1 ];
6103
6104 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6105
6106 exit:
6107 if( err ) Exit( NULL );
6108 }
6109
6110 //===========================================================================================================================
6111 // DNSCryptReceiveResponseHandler
6112 //===========================================================================================================================
6113
6114 static void DNSCryptReceiveResponseHandler( void *inContext )
6115 {
6116 OSStatus err;
6117 struct timeval now;
6118 const uint64_t nowTicks = UpTicks();
6119 SocketContext * const sockCtx = (SocketContext *) inContext;
6120 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6121 sockaddr_ip fromAddr;
6122 DNSCryptResponseHeader * hdr;
6123 const uint8_t * end;
6124 uint8_t * ciphertext;
6125 uint8_t * plaintext;
6126 const uint8_t * response;
6127 uint8_t nonce[ crypto_box_NONCEBYTES ];
6128
6129 gettimeofday( &now, NULL );
6130
6131 dispatch_source_forget( &context->readSource );
6132
6133 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6134 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6135 require_noerr( err, exit );
6136 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6137
6138 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6139 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6140 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6141 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6142
6143 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6144 {
6145 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6146 err = kSizeErr;
6147 goto exit;
6148 }
6149
6150 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6151
6152 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6153 {
6154 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6155 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6156 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6157 err = kValueErr;
6158 goto exit;
6159 }
6160
6161 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6162 {
6163 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6164 err = kValueErr;
6165 goto exit;
6166 }
6167
6168 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6169
6170 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6171 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6172
6173 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6174 check( plaintext == ciphertext );
6175
6176 end = context->msgBuf + context->msgLen;
6177
6178 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6179 require_noerr( err, exit );
6180
6181 response = plaintext + crypto_box_ZEROBYTES;
6182 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6183 Exit( kExitReason_ReceivedResponse );
6184
6185 exit:
6186 if( err ) Exit( NULL );
6187 }
6188
6189 //===========================================================================================================================
6190 // DNSCryptProceed
6191 //===========================================================================================================================
6192
6193 static void DNSCryptProceed( void *inContext )
6194 {
6195 OSStatus err;
6196 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6197
6198 err = DNSCryptProcessCert( context );
6199 require_noerr_quiet( err, exit );
6200
6201 err = DNSCryptBuildQuery( context );
6202 require_noerr_quiet( err, exit );
6203
6204 err = DNSCryptSendQuery( context );
6205 require_noerr_quiet( err, exit );
6206
6207 exit:
6208 if( err ) Exit( NULL );
6209 }
6210
6211 //===========================================================================================================================
6212 // DNSCryptProcessCert
6213 //===========================================================================================================================
6214
6215 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6216 {
6217 OSStatus err;
6218 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6219 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6220 struct timeval now;
6221 time_t startTimeSecs, endTimeSecs;
6222 size_t signedLen;
6223 uint8_t * tempBuf;
6224 unsigned long long tempLen;
6225
6226 DNSCryptPrintCertificate( cert, inContext->certLen );
6227
6228 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6229 {
6230 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6231 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6232 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6233 err = kValueErr;
6234 goto exit;
6235 }
6236
6237 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6238 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6239
6240 gettimeofday( &now, NULL );
6241 if( now.tv_sec < startTimeSecs )
6242 {
6243 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6244 err = kDateErr;
6245 goto exit;
6246 }
6247 if( now.tv_sec >= endTimeSecs )
6248 {
6249 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6250 err = kDateErr;
6251 goto exit;
6252 }
6253
6254 signedLen = (size_t)( certEnd - cert->signature );
6255 tempBuf = (uint8_t *) malloc( signedLen );
6256 require_action( tempBuf, exit, err = kNoMemoryErr );
6257 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6258 free( tempBuf );
6259 if( err )
6260 {
6261 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6262 err = kAuthenticationErr;
6263 goto exit;
6264 }
6265
6266 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6267 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6268
6269 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6270 require_noerr( err, exit );
6271
6272 inContext->certPtr = NULL;
6273 inContext->certLen = 0;
6274 inContext->msgLen = 0;
6275
6276 exit:
6277 return( err );
6278 }
6279
6280 //===========================================================================================================================
6281 // DNSCryptBuildQuery
6282 //===========================================================================================================================
6283
6284 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6285
6286 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6287 {
6288 OSStatus err;
6289 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6290 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6291 size_t queryLen;
6292 size_t paddedQueryLen;
6293 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6294 const uint8_t * padLimit;
6295 uint8_t nonce[ crypto_box_NONCEBYTES ];
6296
6297 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6298
6299 inContext->queryID = (uint16_t) Random32();
6300 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6301 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6302 require_noerr( err, exit );
6303
6304 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6305 if( padLimit > msgLimit ) padLimit = msgLimit;
6306
6307 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6308 require_noerr( err, exit );
6309
6310 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6311 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6312 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6313 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6314
6315 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6316 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6317 require_noerr( err, exit );
6318
6319 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6320 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6321 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6322
6323 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6324
6325 exit:
6326 return( err );
6327 }
6328
6329 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6330 {
6331 OSStatus err;
6332 size_t paddedLen;
6333
6334 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6335
6336 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6337 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6338 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6339 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6340
6341 inMsgPtr[ inMsgLen ] = 0x80;
6342 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6343
6344 if( outPaddedLen ) *outPaddedLen = paddedLen;
6345 err = kNoErr;
6346
6347 exit:
6348 return( err );
6349 }
6350
6351 //===========================================================================================================================
6352 // DNSCryptSendQuery
6353 //===========================================================================================================================
6354
6355 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6356 {
6357 OSStatus err;
6358 SocketContext * sockCtx;
6359 SocketRef sock = kInvalidSocketRef;
6360
6361 check( inContext->msgLen > 0 );
6362 check( !inContext->readSource );
6363
6364 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6365 require_noerr( err, exit );
6366
6367 inContext->sendTicks = UpTicks();
6368 err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6369 require_noerr( err, exit );
6370
6371 err = SocketContextCreate( sock, inContext, &sockCtx );
6372 require_noerr( err, exit );
6373 sock = kInvalidSocketRef;
6374
6375 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6376 &inContext->readSource );
6377 if( err ) ForgetSocketContext( &sockCtx );
6378 require_noerr( err, exit );
6379
6380 dispatch_resume( inContext->readSource );
6381
6382 exit:
6383 ForgetSocket( &sock );
6384 return( err );
6385 }
6386
6387 //===========================================================================================================================
6388 // DNSCryptPrintCertificate
6389 //===========================================================================================================================
6390
6391 #define kCertTimeStrBufLen 32
6392
6393 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6394
6395 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6396 {
6397 time_t startTime, endTime;
6398 int extLen;
6399 char timeBuf[ kCertTimeStrBufLen ];
6400
6401 check( inLen >= kDNSCryptCertMinimumLength );
6402
6403 startTime = (time_t) ReadBig32( inCert->startTime );
6404 endTime = (time_t) ReadBig32( inCert->endTime );
6405
6406 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6407 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6408 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6409 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6410 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6411 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6412 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6413 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6414 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6415 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6416 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6417
6418 if( inLen > kDNSCryptCertMinimumLength )
6419 {
6420 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6421 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6422 }
6423 FPrintF( stdout, "\n" );
6424 }
6425
6426 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6427 {
6428 struct tm * tm;
6429
6430 tm = localtime( &inTime );
6431 if( !tm )
6432 {
6433 dlogassert( "localtime() returned a NULL pointer.\n" );
6434 *inBuffer = '\0';
6435 }
6436 else
6437 {
6438 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6439 }
6440
6441 return( inBuffer );
6442 }
6443
6444 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6445
6446 //===========================================================================================================================
6447 // MDNSQueryCmd
6448 //===========================================================================================================================
6449
6450 typedef struct
6451 {
6452 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6453 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6454 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6455 int localPort; // The port number to which the sockets are bound.
6456 int receiveSecs; // After send, the amount of time to spend receiving.
6457 uint32_t ifIndex; // Index of the interface over which to send the query.
6458 uint16_t qtype; // The type (QTYPE) of the record being queried.
6459 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6460 Boolean allResponses; // True if all mDNS messages received should be printed.
6461 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6462 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6463 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6464 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6465 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6466 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
6467
6468 } MDNSQueryContext;
6469
6470 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6471 static void MDNSQueryReadHandler( void *inContext );
6472
6473 static void MDNSQueryCmd( void )
6474 {
6475 OSStatus err;
6476 MDNSQueryContext * context;
6477 SocketRef sockV4 = kInvalidSocketRef;
6478 SocketRef sockV6 = kInvalidSocketRef;
6479 ssize_t n;
6480 const char * ifname;
6481 size_t msgLen;
6482 unsigned int sendCount;
6483
6484 // Check command parameters.
6485
6486 if( gMDNSQuery_ReceiveSecs < -1 )
6487 {
6488 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6489 err = kParamErr;
6490 goto exit;
6491 }
6492
6493 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6494 require_action( context, exit, err = kNoMemoryErr );
6495
6496 context->qnameStr = gMDNSQuery_Name;
6497 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6498 context->isQU = gMDNSQuery_IsQU ? true : false;
6499 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6500 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6501 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6502 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6503
6504 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6505 require_noerr_quiet( err, exit );
6506
6507 ifname = if_indextoname( context->ifIndex, context->ifName );
6508 require_action( ifname, exit, err = kNameErr );
6509
6510 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6511 require_noerr( err, exit );
6512
6513 // Set up IPv4 socket.
6514
6515 if( context->useIPv4 )
6516 {
6517 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6518 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6519 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
6520 require_noerr( err, exit );
6521 }
6522
6523 // Set up IPv6 socket.
6524
6525 if( context->useIPv6 )
6526 {
6527 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6528 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6529 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
6530 require_noerr( err, exit );
6531 }
6532
6533 // Craft mDNS query message.
6534
6535 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6536 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6537 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6538 require_noerr( err, exit );
6539
6540 // Print prologue.
6541
6542 MDNSQueryPrintPrologue( context );
6543
6544 // Send mDNS query message.
6545
6546 sendCount = 0;
6547 if( IsValidSocket( sockV4 ) )
6548 {
6549 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
6550
6551 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
6552 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6553 if( err )
6554 {
6555 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6556 ForgetSocket( &sockV4 );
6557 }
6558 else
6559 {
6560 ++sendCount;
6561 }
6562 }
6563 if( IsValidSocket( sockV6 ) )
6564 {
6565 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
6566
6567 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
6568 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6569 if( err )
6570 {
6571 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6572 ForgetSocket( &sockV6 );
6573 }
6574 else
6575 {
6576 ++sendCount;
6577 }
6578 }
6579 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6580
6581 // If there's no wait period after the send, then exit.
6582
6583 if( context->receiveSecs == 0 ) goto exit;
6584
6585 // Create dispatch read sources for socket(s).
6586
6587 if( IsValidSocket( sockV4 ) )
6588 {
6589 SocketContext * sockCtx;
6590
6591 err = SocketContextCreate( sockV4, context, &sockCtx );
6592 require_noerr( err, exit );
6593 sockV4 = kInvalidSocketRef;
6594
6595 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6596 &context->readSourceV4 );
6597 if( err ) ForgetSocketContext( &sockCtx );
6598 require_noerr( err, exit );
6599
6600 dispatch_resume( context->readSourceV4 );
6601 }
6602
6603 if( IsValidSocket( sockV6 ) )
6604 {
6605 SocketContext * sockCtx;
6606
6607 err = SocketContextCreate( sockV6, context, &sockCtx );
6608 require_noerr( err, exit );
6609 sockV6 = kInvalidSocketRef;
6610
6611 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6612 &context->readSourceV6 );
6613 if( err ) ForgetSocketContext( &sockCtx );
6614 require_noerr( err, exit );
6615
6616 dispatch_resume( context->readSourceV6 );
6617 }
6618
6619 if( context->receiveSecs > 0 )
6620 {
6621 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6622 Exit );
6623 }
6624 dispatch_main();
6625
6626 exit:
6627 ForgetSocket( &sockV4 );
6628 ForgetSocket( &sockV6 );
6629 if( err ) exit( 1 );
6630 }
6631
6632 //===========================================================================================================================
6633 // MDNSColliderCmd
6634 //===========================================================================================================================
6635
6636 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
6637
6638 static void MDNSColliderCmd( void )
6639 {
6640 OSStatus err;
6641 MDNSColliderRef collider = NULL;
6642 uint8_t * rdataPtr = NULL;
6643 size_t rdataLen = 0;
6644 const char * ifname;
6645 uint32_t ifIndex;
6646 MDNSColliderProtocols protocols;
6647 uint16_t type;
6648 char ifName[ IF_NAMESIZE + 1 ];
6649 uint8_t name[ kDomainNameLengthMax ];
6650
6651 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6652 require_noerr_quiet( err, exit );
6653
6654 ifname = if_indextoname( ifIndex, ifName );
6655 if( !ifname )
6656 {
6657 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
6658 err = kNameErr;
6659 goto exit;
6660 }
6661
6662 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
6663 if( err )
6664 {
6665 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
6666 goto exit;
6667 }
6668
6669 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
6670 require_noerr_quiet( err, exit );
6671
6672 if( gMDNSCollider_RecordData )
6673 {
6674 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
6675 require_noerr_quiet( err, exit );
6676 }
6677
6678 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
6679 require_noerr( err, exit );
6680
6681 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
6682 if( err )
6683 {
6684 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
6685 goto exit;
6686 }
6687
6688 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
6689 require_noerr( err, exit );
6690 ForgetMem( &rdataPtr );
6691
6692 protocols = kMDNSColliderProtocol_None;
6693 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
6694 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
6695 MDNSColliderSetProtocols( collider, protocols );
6696 MDNSColliderSetInterfaceIndex( collider, ifIndex );
6697 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
6698
6699 err = MDNSColliderStart( collider );
6700 require_noerr( err, exit );
6701
6702 dispatch_main();
6703
6704 exit:
6705 FreeNullSafe( rdataPtr );
6706 CFReleaseNullSafe( collider );
6707 if( err ) exit( 1 );
6708 }
6709
6710 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
6711 {
6712 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
6713
6714 CFRelease( collider );
6715 exit( inError ? 1 : 0 );
6716 }
6717
6718 //===========================================================================================================================
6719 // MDNSQueryPrintPrologue
6720 //===========================================================================================================================
6721
6722 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6723 {
6724 const int receiveSecs = inContext->receiveSecs;
6725
6726 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6727 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6728 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6729 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6730 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6731 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6732 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6733 FPrintF( stdout, "Receive duration: " );
6734 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6735 else FPrintF( stdout, "∞\n" );
6736 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6737 }
6738
6739 //===========================================================================================================================
6740 // MDNSQueryReadHandler
6741 //===========================================================================================================================
6742
6743 static void MDNSQueryReadHandler( void *inContext )
6744 {
6745 OSStatus err;
6746 struct timeval now;
6747 SocketContext * const sockCtx = (SocketContext *) inContext;
6748 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
6749 size_t msgLen;
6750 sockaddr_ip fromAddr;
6751 Boolean foundAnswer = false;
6752
6753 gettimeofday( &now, NULL );
6754
6755 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6756 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6757 require_noerr( err, exit );
6758
6759 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6760 {
6761 const uint8_t * ptr;
6762 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6763 unsigned int rrCount, i;
6764 uint16_t type, class;
6765 uint8_t name[ kDomainNameLengthMax ];
6766
6767 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6768 require_noerr( err, exit );
6769
6770 if( context->qname[ 0 ] == 0 )
6771 {
6772 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6773 require_noerr( err, exit );
6774 }
6775
6776 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6777 for( i = 0; i < rrCount; ++i )
6778 {
6779 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6780 require_noerr( err, exit );
6781
6782 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6783 DomainNameEqual( name, context->qname ) )
6784 {
6785 foundAnswer = true;
6786 break;
6787 }
6788 }
6789 }
6790 if( context->allResponses || foundAnswer )
6791 {
6792 FPrintF( stdout, "---\n" );
6793 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6794 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6795 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6796 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
6797 }
6798
6799 exit:
6800 if( err ) exit( 1 );
6801 }
6802
6803 //===========================================================================================================================
6804 // PIDToUUIDCmd
6805 //===========================================================================================================================
6806
6807 static void PIDToUUIDCmd( void )
6808 {
6809 OSStatus err;
6810 int n;
6811 struct proc_uniqidentifierinfo info;
6812
6813 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6814 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6815
6816 FPrintF( stdout, "%#U\n", info.p_uuid );
6817 err = kNoErr;
6818
6819 exit:
6820 if( err ) exit( 1 );
6821 }
6822
6823 //===========================================================================================================================
6824 // DNSServerCmd
6825 //===========================================================================================================================
6826
6827 typedef struct DNSServerPrivate * DNSServerRef;
6828
6829 typedef struct
6830 {
6831 DNSServerRef server; // Reference to the DNS server.
6832 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
6833 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
6834 const char * domainOverride; // If non-NULL, the server is to use this domain instead of "d.test.".
6835 #if( TARGET_OS_DARWIN )
6836 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
6837 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
6838 Boolean addedResolver; // True if system DNS settings contains a resolver entry for server.
6839 #endif
6840 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
6841
6842 } DNSServerCmdContext;
6843
6844 typedef enum
6845 {
6846 kDNSServerEvent_Started = 1,
6847 kDNSServerEvent_Stopped = 2
6848
6849 } DNSServerEventType;
6850
6851 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6852
6853 CFTypeID DNSServerGetTypeID( void );
6854 static OSStatus
6855 DNSServerCreate(
6856 dispatch_queue_t inQueue,
6857 DNSServerEventHandler_f inEventHandler,
6858 void * inEventContext,
6859 unsigned int inResponseDelayMs,
6860 uint32_t inDefaultTTL,
6861 int inPort,
6862 Boolean inLoopbackOnly,
6863 const char * inDomain,
6864 Boolean inBadUDPMode,
6865 DNSServerRef * outServer );
6866 static void DNSServerStart( DNSServerRef inServer );
6867 static void DNSServerStop( DNSServerRef inServer );
6868
6869 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6870
6871 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
6872 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6873 static void DNSServerCmdSigIntHandler( void *inContext );
6874 static void DNSServerCmdSigTermHandler( void *inContext );
6875 #if( TARGET_OS_DARWIN )
6876 static void DNSServerCmdFollowedProcessHandler( void *inContext );
6877 #endif
6878
6879 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
6880 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6881
6882 static void DNSServerCmd( void )
6883 {
6884 OSStatus err;
6885 DNSServerCmdContext * context = NULL;
6886
6887 if( gDNSServer_Foreground )
6888 {
6889 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6890 }
6891
6892 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
6893 require_noerr_quiet( err, exit );
6894
6895 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
6896 require_noerr_quiet( err, exit );
6897
6898 err = CheckIntegerArgument( gDNSServer_Port, "port number", -UINT16_MAX, UINT16_MAX );
6899 require_noerr_quiet( err, exit );
6900
6901 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
6902 require_action( context, exit, err = kNoMemoryErr );
6903
6904 context->domainOverride = gDNSServer_DomainOverride;
6905 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
6906
6907 #if( TARGET_OS_DARWIN )
6908 if( gDNSServer_FollowPID )
6909 {
6910 context->followPID = _StringToPID( gDNSServer_FollowPID, &err );
6911 if( err || ( context->followPID < 0 ) )
6912 {
6913 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
6914 err = kParamErr;
6915 goto exit;
6916 }
6917
6918 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
6919 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
6920 require_noerr( err, exit );
6921 dispatch_resume( context->processMonitor );
6922 }
6923 else
6924 {
6925 context->followPID = -1;
6926 }
6927 #endif
6928
6929 signal( SIGINT, SIG_IGN );
6930 err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
6931 require_noerr( err, exit );
6932 dispatch_resume( context->sigIntSource );
6933
6934 signal( SIGTERM, SIG_IGN );
6935 err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
6936 require_noerr( err, exit );
6937 dispatch_resume( context->sigTermSource );
6938
6939 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context,
6940 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, gDNSServer_Port, context->loopbackOnly,
6941 context->domainOverride, gDNSServer_BadUDPMode ? true : false, &context->server );
6942 require_noerr( err, exit );
6943
6944 DNSServerStart( context->server );
6945 dispatch_main();
6946
6947 exit:
6948 FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
6949 if( context ) DNSServerCmdContextFree( context );
6950 if( err ) exit( 1 );
6951 }
6952
6953 //===========================================================================================================================
6954 // DNSServerCmdContextFree
6955 //===========================================================================================================================
6956
6957 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
6958 {
6959 ForgetCF( &inContext->server );
6960 dispatch_source_forget( &inContext->sigIntSource );
6961 dispatch_source_forget( &inContext->sigTermSource );
6962 #if( TARGET_OS_DARWIN )
6963 dispatch_source_forget( &inContext->processMonitor );
6964 #endif
6965 free( inContext );
6966 }
6967
6968 //===========================================================================================================================
6969 // DNSServerCmdEventHandler
6970 //===========================================================================================================================
6971
6972 #if( TARGET_OS_DARWIN )
6973 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort );
6974 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
6975 #endif
6976
6977 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext )
6978 {
6979 OSStatus err;
6980 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
6981
6982 if( inType == kDNSServerEvent_Started )
6983 {
6984 #if( TARGET_OS_DARWIN )
6985 const int port = (int) inEventData;
6986
6987 err = _DNSServerCmdLoopbackResolverAdd( context->domainOverride ? context->domainOverride : "d.test.", port );
6988 if( err )
6989 {
6990 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
6991 err );
6992 if( context->loopbackOnly ) ForgetDNSServer( &context->server );
6993 }
6994 else
6995 {
6996 context->addedResolver = true;
6997 }
6998 #endif
6999 }
7000 else if( inType == kDNSServerEvent_Stopped )
7001 {
7002 const OSStatus stopError = (OSStatus) inEventData;
7003
7004 if( stopError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", stopError );
7005
7006 err = kNoErr;
7007 #if( TARGET_OS_DARWIN )
7008 if( context->addedResolver )
7009 {
7010 err = _DNSServerCmdLoopbackResolverRemove();
7011 if( err )
7012 {
7013 ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7014 }
7015 else
7016 {
7017 context->addedResolver = false;
7018 }
7019 }
7020 else if( context->loopbackOnly )
7021 {
7022 err = kUnknownErr;
7023 }
7024 #endif
7025 DNSServerCmdContextFree( context );
7026 exit( ( stopError || err ) ? 1 : 0 );
7027 }
7028 }
7029
7030 #if( TARGET_OS_DARWIN )
7031 //===========================================================================================================================
7032 // _DNSServerCmdLoopbackResolverAdd
7033 //===========================================================================================================================
7034
7035 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort )
7036 {
7037 OSStatus err;
7038 SCDynamicStoreRef store;
7039 CFPropertyListRef plist = NULL;
7040 CFStringRef key = NULL;
7041 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7042 Boolean success;
7043
7044 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7045 err = map_scerror( store );
7046 require_noerr( err, exit );
7047
7048 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7049 "{"
7050 "%kO="
7051 "["
7052 "%s"
7053 "]"
7054 "%kO="
7055 "["
7056 "%.4a"
7057 "%.16a"
7058 "]"
7059 "%kO=%i"
7060 "%kO=%O"
7061 "%kO=%O"
7062 "}",
7063 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7064 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr,
7065 kSCPropNetDNSServerPort, inPort,
7066 kSCPropInterfaceName, CFSTR( "lo0" ),
7067 kSCPropNetDNSConfirmedServiceID, CFSTR( "com.apple.dnssdutil.server" ) );
7068 require_noerr( err, exit );
7069
7070 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7071 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7072 require_action( key, exit, err = kUnknownErr );
7073
7074 success = SCDynamicStoreSetValue( store, key, plist );
7075 require_action( success, exit, err = kUnknownErr );
7076
7077 exit:
7078 CFReleaseNullSafe( store );
7079 CFReleaseNullSafe( plist );
7080 CFReleaseNullSafe( key );
7081 return( err );
7082 }
7083
7084 //===========================================================================================================================
7085 // _DNSServerCmdLoopbackResolverRemove
7086 //===========================================================================================================================
7087
7088 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7089 {
7090 OSStatus err;
7091 SCDynamicStoreRef store;
7092 CFStringRef key = NULL;
7093 Boolean success;
7094
7095 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7096 err = map_scerror( store );
7097 require_noerr( err, exit );
7098
7099 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7100 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7101 require_action( key, exit, err = kUnknownErr );
7102
7103 success = SCDynamicStoreRemoveValue( store, key );
7104 require_action( success, exit, err = kUnknownErr );
7105
7106 exit:
7107 CFReleaseNullSafe( store );
7108 CFReleaseNullSafe( key );
7109 return( err );
7110 }
7111 #endif
7112
7113 //===========================================================================================================================
7114 // DNSServerCmdSigIntHandler
7115 //===========================================================================================================================
7116
7117 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal );
7118
7119 static void DNSServerCmdSigIntHandler( void *inContext )
7120 {
7121 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGINT );
7122 }
7123
7124 //===========================================================================================================================
7125 // DNSServerCmdSigTermHandler
7126 //===========================================================================================================================
7127
7128 static void DNSServerCmdSigTermHandler( void *inContext )
7129 {
7130 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGTERM );
7131 }
7132
7133 #if( TARGET_OS_DARWIN )
7134 //===========================================================================================================================
7135 // DNSServerCmdFollowedProcessHandler
7136 //===========================================================================================================================
7137
7138 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7139 {
7140 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7141
7142 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( context, 0 );
7143 }
7144 #endif
7145
7146 //===========================================================================================================================
7147 // _DNSServerCmdExternalExit
7148 //===========================================================================================================================
7149
7150 #define SignalNumberToString( X ) ( \
7151 ( (X) == SIGINT ) ? "SIGINT" : \
7152 ( (X) == SIGTERM ) ? "SIGTERM" : \
7153 "???" )
7154
7155 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal )
7156 {
7157 dispatch_source_forget( &inContext->sigIntSource );
7158 dispatch_source_forget( &inContext->sigTermSource );
7159 #if( TARGET_OS_DARWIN )
7160 dispatch_source_forget( &inContext->processMonitor );
7161
7162 if( inSignal == 0 )
7163 {
7164 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7165 }
7166 else
7167 #endif
7168 {
7169 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7170 }
7171
7172 ForgetDNSServer( &inContext->server );
7173 }
7174
7175 //===========================================================================================================================
7176 // DNSServerCreate
7177 //===========================================================================================================================
7178
7179 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7180
7181 typedef struct DNSDelayedResponse DNSDelayedResponse;
7182 struct DNSDelayedResponse
7183 {
7184 DNSDelayedResponse * next;
7185 sockaddr_ip destAddr;
7186 uint64_t targetTicks;
7187 uint8_t * msgPtr;
7188 size_t msgLen;
7189 };
7190
7191 struct DNSServerPrivate
7192 {
7193 CFRuntimeBase base; // CF object base.
7194 uint8_t * domain; // Parent domain of server's resource records.
7195 dispatch_queue_t queue; // Queue for DNS server's events.
7196 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7197 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7198 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7199 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7200 SocketRef sockUDPv4;
7201 SocketRef sockUDPv6;
7202 DNSServerEventHandler_f eventHandler;
7203 void * eventContext;
7204 DNSDelayedResponse * responseList;
7205 dispatch_source_t responseTimer;
7206 unsigned int responseDelayMs;
7207 uint32_t defaultTTL;
7208 uint32_t serial; // Serial number for SOA record.
7209 int port; // Port to use for receiving and sending DNS messages.
7210 OSStatus stopError;
7211 Boolean stopped;
7212 Boolean loopbackOnly;
7213 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
7214 };
7215
7216 static void _DNSServerUDPReadHandler( void *inContext );
7217 static void _DNSServerTCPReadHandler( void *inContext );
7218 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse );
7219 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList );
7220
7221 CF_CLASS_DEFINE( DNSServer );
7222
7223 static OSStatus
7224 DNSServerCreate(
7225 dispatch_queue_t inQueue,
7226 DNSServerEventHandler_f inEventHandler,
7227 void * inEventContext,
7228 unsigned int inResponseDelayMs,
7229 uint32_t inDefaultTTL,
7230 int inPort,
7231 Boolean inLoopbackOnly,
7232 const char * inDomain,
7233 Boolean inBadUDPMode,
7234 DNSServerRef * outServer )
7235 {
7236 OSStatus err;
7237 DNSServerRef obj = NULL;
7238
7239 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
7240
7241 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7242
7243 ReplaceDispatchQueue( &obj->queue, inQueue );
7244 obj->eventHandler = inEventHandler;
7245 obj->eventContext = inEventContext;
7246 obj->responseDelayMs = inResponseDelayMs;
7247 obj->defaultTTL = inDefaultTTL;
7248 obj->port = inPort;
7249 obj->loopbackOnly = inLoopbackOnly;
7250 obj->badUDPMode = inBadUDPMode;
7251
7252 if( inDomain )
7253 {
7254 err = StringToDomainName( inDomain, &obj->domain, NULL );
7255 require_noerr_quiet( err, exit );
7256 }
7257 else
7258 {
7259 err = DomainNameDup( kDDotTestDomainName, &obj->domain, NULL );
7260 require_noerr_quiet( err, exit );
7261 }
7262
7263 *outServer = obj;
7264 obj = NULL;
7265 err = kNoErr;
7266
7267 exit:
7268 CFReleaseNullSafe( obj );
7269 return( err );
7270 }
7271
7272 //===========================================================================================================================
7273 // _DNSServerFinalize
7274 //===========================================================================================================================
7275
7276 static void _DNSServerFinalize( CFTypeRef inObj )
7277 {
7278 DNSServerRef const me = (DNSServerRef) inObj;
7279
7280 check( !me->readSourceUDPv4 );
7281 check( !me->readSourceUDPv6 );
7282 check( !me->readSourceTCPv4 );
7283 check( !me->readSourceTCPv6 );
7284 check( !me->responseTimer );
7285 ForgetMem( &me->domain );
7286 dispatch_forget( &me->queue );
7287 }
7288
7289 //===========================================================================================================================
7290 // DNSServerStart
7291 //===========================================================================================================================
7292
7293 static void _DNSServerStart( void *inContext );
7294 static void _DNSServerStop( void *inContext, OSStatus inError );
7295
7296 static void DNSServerStart( DNSServerRef me )
7297 {
7298 CFRetain( me );
7299 dispatch_async_f( me->queue, me, _DNSServerStart );
7300 }
7301
7302 static void _DNSServerStart( void *inContext )
7303 {
7304 OSStatus err;
7305 struct timeval now;
7306 DNSServerRef const me = (DNSServerRef) inContext;
7307 SocketRef sock = kInvalidSocketRef;
7308 SocketContext * sockCtx = NULL;
7309 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7310 int year, month, day;
7311
7312 // Create IPv4 UDP socket.
7313 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7314 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7315 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7316 // used for the remaining sockets.
7317
7318 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7319 me->port, &me->port, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7320 require_noerr( err, exit );
7321 check( me->port > 0 );
7322
7323 // Create read source for IPv4 UDP socket.
7324
7325 err = SocketContextCreate( sock, me, &sockCtx );
7326 require_noerr( err, exit );
7327 sock = kInvalidSocketRef;
7328
7329 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7330 &me->readSourceUDPv4 );
7331 require_noerr( err, exit );
7332 dispatch_resume( me->readSourceUDPv4 );
7333 me->sockUDPv4 = sockCtx->sock;
7334 sockCtx = NULL;
7335
7336 // Create IPv6 UDP socket.
7337
7338 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7339 me->port, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7340 require_noerr( err, exit );
7341
7342 // Create read source for IPv6 UDP socket.
7343
7344 err = SocketContextCreate( sock, me, &sockCtx );
7345 require_noerr( err, exit );
7346 sock = kInvalidSocketRef;
7347
7348 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7349 &me->readSourceUDPv6 );
7350 require_noerr( err, exit );
7351 dispatch_resume( me->readSourceUDPv6 );
7352 me->sockUDPv6 = sockCtx->sock;
7353 sockCtx = NULL;
7354
7355 // Create IPv4 TCP socket.
7356
7357 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7358 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7359 require_noerr( err, exit );
7360
7361 // Create read source for IPv4 TCP socket.
7362
7363 err = SocketContextCreate( sock, me, &sockCtx );
7364 require_noerr( err, exit );
7365 sock = kInvalidSocketRef;
7366
7367 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7368 &me->readSourceTCPv4 );
7369 require_noerr( err, exit );
7370 dispatch_resume( me->readSourceTCPv4 );
7371 sockCtx = NULL;
7372
7373 // Create IPv6 TCP socket.
7374
7375 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7376 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7377 require_noerr( err, exit );
7378
7379 // Create read source for IPv6 TCP socket.
7380
7381 err = SocketContextCreate( sock, me, &sockCtx );
7382 require_noerr( err, exit );
7383 sock = kInvalidSocketRef;
7384
7385 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7386 &me->readSourceTCPv6 );
7387 require_noerr( err, exit );
7388 dispatch_resume( me->readSourceTCPv6 );
7389 sockCtx = NULL;
7390
7391 ds_ulog( kLogLevelInfo, "Server is using port %d.\n", me->port );
7392 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, (uintptr_t) me->port, me->eventContext );
7393
7394 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7395 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7396
7397 gettimeofday( &now, NULL );
7398 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
7399 NULL, NULL, NULL );
7400 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
7401
7402 exit:
7403 ForgetSocket( &sock );
7404 if( sockCtx ) SocketContextRelease( sockCtx );
7405 if( err ) _DNSServerStop( me, err );
7406 }
7407
7408 //===========================================================================================================================
7409 // DNSServerStop
7410 //===========================================================================================================================
7411
7412 static void _DNSServerUserStop( void *inContext );
7413 static void _DNSServerStop2( void *inContext );
7414
7415 static void DNSServerStop( DNSServerRef me )
7416 {
7417 CFRetain( me );
7418 dispatch_async_f( me->queue, me, _DNSServerUserStop );
7419 }
7420
7421 static void _DNSServerUserStop( void *inContext )
7422 {
7423 DNSServerRef const me = (DNSServerRef) inContext;
7424
7425 _DNSServerStop( me, kNoErr );
7426 CFRelease( me );
7427 }
7428
7429 static void _DNSServerStop( void *inContext, OSStatus inError )
7430 {
7431 DNSServerRef const me = (DNSServerRef) inContext;
7432
7433 me->stopError = inError;
7434 dispatch_source_forget( &me->readSourceUDPv4 );
7435 dispatch_source_forget( &me->readSourceUDPv6 );
7436 dispatch_source_forget( &me->readSourceTCPv4 );
7437 dispatch_source_forget( &me->readSourceTCPv6 );
7438 dispatch_source_forget( &me->responseTimer );
7439 me->sockUDPv4 = kInvalidSocketRef;
7440 me->sockUDPv6 = kInvalidSocketRef;
7441
7442 if( me->responseList )
7443 {
7444 _DNSDelayedResponseFreeList( me->responseList );
7445 me->responseList = NULL;
7446 }
7447 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7448 }
7449
7450 static void _DNSServerStop2( void *inContext )
7451 {
7452 DNSServerRef const me = (DNSServerRef) inContext;
7453
7454 if( !me->stopped )
7455 {
7456 me->stopped = true;
7457 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, (uintptr_t) me->stopError, me->eventContext );
7458 CFRelease( me );
7459 }
7460 CFRelease( me );
7461 }
7462
7463 //===========================================================================================================================
7464 // _DNSDelayedResponseFree
7465 //===========================================================================================================================
7466
7467 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse )
7468 {
7469 ForgetMem( &inResponse->msgPtr );
7470 free( inResponse );
7471 }
7472
7473 //===========================================================================================================================
7474 // _DNSDelayedResponseFreeList
7475 //===========================================================================================================================
7476
7477 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList )
7478 {
7479 DNSDelayedResponse * response;
7480
7481 while( ( response = inList ) != NULL )
7482 {
7483 inList = response->next;
7484 _DNSDelayedResponseFree( response );
7485 }
7486 }
7487
7488 //===========================================================================================================================
7489 // _DNSServerUDPReadHandler
7490 //===========================================================================================================================
7491
7492 static OSStatus
7493 _DNSServerAnswerQuery(
7494 DNSServerRef inServer,
7495 const uint8_t * inQueryPtr,
7496 size_t inQueryLen,
7497 Boolean inForTCP,
7498 uint8_t ** outResponsePtr,
7499 size_t * outResponseLen );
7500
7501 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7502 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7503
7504 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7505 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7506
7507 static OSStatus
7508 _DNSServerScheduleDelayedResponse(
7509 DNSServerRef inServer,
7510 const struct sockaddr * inDestAddr,
7511 uint8_t * inMsgPtr,
7512 size_t inMsgLen );
7513 static void _DNSServerUDPDelayedSend( void *inContext );
7514
7515 static void _DNSServerUDPReadHandler( void *inContext )
7516 {
7517 OSStatus err;
7518 SocketContext * const sockCtx = (SocketContext *) inContext;
7519 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7520 struct timeval now;
7521 ssize_t n;
7522 sockaddr_ip clientAddr;
7523 socklen_t clientAddrLen;
7524 uint8_t * responsePtr = NULL; // malloc'd
7525 size_t responseLen;
7526 uint8_t msg[ 512 ];
7527
7528 gettimeofday( &now, NULL );
7529
7530 // Receive message.
7531
7532 clientAddrLen = (socklen_t) sizeof( clientAddr );
7533 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7534 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7535 require_noerr( err, exit );
7536
7537 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7538
7539 if( n < kDNSHeaderLength )
7540 {
7541 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7542 goto exit;
7543 }
7544
7545 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7546
7547 // Create response.
7548
7549 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, &responsePtr, &responseLen );
7550 require_noerr_quiet( err, exit );
7551
7552 // Schedule response.
7553
7554 if( me->responseDelayMs > 0 )
7555 {
7556 err = _DNSServerScheduleDelayedResponse( me, &clientAddr.sa, responsePtr, responseLen );
7557 require_noerr( err, exit );
7558 responsePtr = NULL;
7559 }
7560 else
7561 {
7562 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7563
7564 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7565 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7566 require_noerr( err, exit );
7567 }
7568
7569 exit:
7570 FreeNullSafe( responsePtr );
7571 return;
7572 }
7573
7574 static OSStatus
7575 _DNSServerScheduleDelayedResponse(
7576 DNSServerRef me,
7577 const struct sockaddr * inDestAddr,
7578 uint8_t * inMsgPtr,
7579 size_t inMsgLen )
7580 {
7581 OSStatus err;
7582 DNSDelayedResponse * response;
7583 DNSDelayedResponse ** responsePtr;
7584 DNSDelayedResponse * newResponse;
7585 uint64_t targetTicks;
7586
7587 targetTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
7588
7589 newResponse = (DNSDelayedResponse *) calloc( 1, sizeof( *newResponse ) );
7590 require_action( newResponse, exit, err = kNoMemoryErr );
7591
7592 if( !me->responseList || ( targetTicks < me->responseList->targetTicks ) )
7593 {
7594 dispatch_source_forget( &me->responseTimer );
7595
7596 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7597 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, _DNSServerUDPDelayedSend,
7598 NULL, me, &me->responseTimer );
7599 require_noerr( err, exit );
7600 dispatch_resume( me->responseTimer );
7601 }
7602
7603 SockAddrCopy( inDestAddr, &newResponse->destAddr );
7604 newResponse->targetTicks = targetTicks;
7605 newResponse->msgPtr = inMsgPtr;
7606 newResponse->msgLen = inMsgLen;
7607
7608 for( responsePtr = &me->responseList; ( response = *responsePtr ) != NULL; responsePtr = &response->next )
7609 {
7610 if( newResponse->targetTicks < response->targetTicks ) break;
7611 }
7612 newResponse->next = response;
7613 *responsePtr = newResponse;
7614 newResponse = NULL;
7615 err = kNoErr;
7616
7617 exit:
7618 if( newResponse ) _DNSDelayedResponseFree( newResponse );
7619 return( err );
7620 }
7621
7622 static void _DNSServerUDPDelayedSend( void *inContext )
7623 {
7624 OSStatus err;
7625 DNSServerRef const me = (DNSServerRef) inContext;
7626 DNSDelayedResponse * response;
7627 SocketRef sock;
7628 ssize_t n;
7629 uint64_t nowTicks;
7630 uint64_t remainingNs;
7631 DNSDelayedResponse * freeList = NULL;
7632
7633 dispatch_source_forget( &me->responseTimer );
7634
7635 nowTicks = UpTicks();
7636 while( ( ( response = me->responseList ) != NULL ) && ( response->targetTicks <= nowTicks ) )
7637 {
7638 me->responseList = response->next;
7639
7640 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7641 response->msgLen, response->msgPtr, response->msgLen );
7642
7643 sock = ( response->destAddr.sa.sa_family == AF_INET ) ? me->sockUDPv4 : me->sockUDPv6;
7644 n = sendto( sock, (char *) response->msgPtr, response->msgLen, 0, &response->destAddr.sa,
7645 SockAddrGetSize( &response->destAddr ) );
7646 err = map_socket_value_errno( sock, n == (ssize_t) response->msgLen, n );
7647 check_noerr( err );
7648
7649 response->next = freeList;
7650 freeList = response;
7651 nowTicks = UpTicks();
7652 }
7653
7654 if( response )
7655 {
7656 check( response->targetTicks > nowTicks );
7657 remainingNs = UpTicksToNanoseconds( response->targetTicks - nowTicks );
7658 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7659
7660 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7661 me->queue, _DNSServerUDPDelayedSend, NULL, me, &me->responseTimer );
7662 require_noerr( err, exit );
7663 dispatch_resume( me->responseTimer );
7664 }
7665
7666 exit:
7667 if( freeList ) _DNSDelayedResponseFreeList( freeList );
7668 }
7669
7670 //===========================================================================================================================
7671 // _DNSServerAnswerQuery
7672 //===========================================================================================================================
7673
7674 #define kLabelPrefix_Alias "alias"
7675 #define kLabelPrefix_AliasTTL "alias-ttl"
7676 #define kLabelPrefix_Count "count-"
7677 #define kLabelPrefix_Tag "tag-"
7678 #define kLabelPrefix_TTL "ttl-"
7679 #define kLabel_IPv4 "ipv4"
7680 #define kLabel_IPv6 "ipv6"
7681 #define kLabelPrefix_SRV "srv-"
7682
7683 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7684 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7685
7686 typedef struct
7687 {
7688 uint16_t priority; // Priority from SRV label.
7689 uint16_t weight; // Weight from SRV label.
7690 uint16_t port; // Port number from SRV label.
7691 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
7692 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
7693
7694 } ParsedSRV;
7695
7696 static OSStatus
7697 _DNSServerInitializeResponseMessage(
7698 DataBuffer * inDB,
7699 unsigned int inID,
7700 unsigned int inFlags,
7701 const uint8_t * inQName,
7702 unsigned int inQType,
7703 unsigned int inQClass );
7704 static OSStatus
7705 _DNSServerAnswerQueryDynamically(
7706 DNSServerRef inServer,
7707 const uint8_t * inQName,
7708 unsigned int inQType,
7709 unsigned int inQClass,
7710 Boolean inForTCP,
7711 DataBuffer * inDB );
7712 static Boolean
7713 _DNSServerNameIsSRVName(
7714 DNSServerRef inServer,
7715 const uint8_t * inName,
7716 const uint8_t ** outDomainPtr,
7717 size_t * outDomainLen,
7718 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
7719 size_t * outSRVCount );
7720 static Boolean
7721 _DNSServerNameIsHostname(
7722 DNSServerRef inServer,
7723 const uint8_t * inName,
7724 uint32_t * outAliasCount,
7725 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
7726 size_t * outAliasTTLCount,
7727 unsigned int * outCount,
7728 unsigned int * outRandCount,
7729 uint32_t * outTTL,
7730 Boolean * outHasA,
7731 Boolean * outHasAAAA,
7732 Boolean * outHasSOA );
7733
7734 static OSStatus
7735 _DNSServerAnswerQuery(
7736 DNSServerRef me,
7737 const uint8_t * const inQueryPtr,
7738 const size_t inQueryLen,
7739 Boolean inForTCP,
7740 uint8_t ** outResponsePtr,
7741 size_t * outResponseLen )
7742 {
7743 OSStatus err;
7744 DataBuffer dataBuf;
7745 const uint8_t * ptr;
7746 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
7747 const DNSHeader * qhdr;
7748 const dns_fixed_fields_question * fields;
7749 unsigned int msgID, qflags, qtype, qclass, rflags;
7750 uint8_t qname[ kDomainNameLengthMax ];
7751
7752 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
7753
7754 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
7755
7756 qhdr = (const DNSHeader *) inQueryPtr;
7757 msgID = DNSHeaderGetID( qhdr );
7758 qflags = DNSHeaderGetFlags( qhdr );
7759
7760 // Minimal checking of the query message's header.
7761
7762 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
7763 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
7764 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
7765 {
7766 err = kRequestErr;
7767 goto exit;
7768 }
7769
7770 // Get QNAME.
7771
7772 ptr = (const uint8_t *) &qhdr[ 1 ];
7773 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
7774 require_noerr( err, exit );
7775
7776 // Get QTYPE and QCLASS.
7777
7778 require_action_quiet( ( (size_t)( queryEnd - ptr ) ) >= sizeof( *fields ), exit, err = kUnderrunErr );
7779 fields = (const dns_fixed_fields_question *) ptr;
7780 qtype = dns_fixed_fields_question_get_type( fields );
7781 qclass = dns_fixed_fields_question_get_class( fields );
7782
7783 // Create a tentative response message.
7784
7785 rflags = kDNSHeaderFlag_Response;
7786 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
7787 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
7788
7789 if( me->badUDPMode && !inForTCP ) msgID = (uint16_t)( msgID + 1 );
7790 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7791 require_noerr( err, exit );
7792
7793 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inForTCP, &dataBuf );
7794 if( err )
7795 {
7796 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
7797 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7798 require_noerr( err, exit );
7799 }
7800
7801 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
7802 require_noerr( err, exit );
7803
7804 exit:
7805 DataBuffer_Free( &dataBuf );
7806 return( err );
7807 }
7808
7809 static OSStatus
7810 _DNSServerInitializeResponseMessage(
7811 DataBuffer * inDB,
7812 unsigned int inID,
7813 unsigned int inFlags,
7814 const uint8_t * inQName,
7815 unsigned int inQType,
7816 unsigned int inQClass )
7817 {
7818 OSStatus err;
7819 DNSHeader header;
7820
7821 DataBuffer_Reset( inDB );
7822
7823 memset( &header, 0, sizeof( header ) );
7824 DNSHeaderSetID( &header, inID );
7825 DNSHeaderSetFlags( &header, inFlags );
7826 DNSHeaderSetQuestionCount( &header, 1 );
7827
7828 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
7829 require_noerr( err, exit );
7830
7831 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), (uint16_t) inQType,
7832 (uint16_t) inQClass );
7833 require_noerr( err, exit );
7834
7835 exit:
7836 return( err );
7837 }
7838
7839 static OSStatus
7840 _DNSServerAnswerQueryDynamically(
7841 DNSServerRef me,
7842 const uint8_t * const inQName,
7843 const unsigned int inQType,
7844 const unsigned int inQClass,
7845 const Boolean inForTCP,
7846 DataBuffer * const inDB )
7847 {
7848 OSStatus err;
7849 DNSHeader * hdr;
7850 unsigned int flags, rcode;
7851 uint32_t aliasCount, i;
7852 uint32_t aliasTTLs[ kMaxAliasTTLCount ];
7853 size_t aliasTTLCount;
7854 unsigned int addrCount, randCount;
7855 uint32_t ttl;
7856 ParsedSRV srvArray[ kMaxParsedSRVCount ];
7857 size_t srvCount;
7858 const uint8_t * srvDomainPtr;
7859 size_t srvDomainLen;
7860 unsigned int answerCount;
7861 Boolean notImplemented, truncated;
7862 Boolean useAliasTTLs, nameExists, nameHasA, nameHasAAAA, nameHasSRV, nameHasSOA;
7863 uint8_t namePtr[ 2 ];
7864 dns_fixed_fields_record fields;
7865
7866 answerCount = 0;
7867 truncated = false;
7868 nameExists = false;
7869 require_action_quiet( inQClass == kDNSServiceClass_IN, done, notImplemented = true );
7870
7871 notImplemented = false;
7872 aliasCount = 0;
7873 nameHasA = false;
7874 nameHasAAAA = false;
7875 nameHasSOA = false;
7876 useAliasTTLs = false;
7877 nameHasSRV = false;
7878 srvDomainLen = 0;
7879 srvCount = 0;
7880
7881 if( _DNSServerNameIsHostname( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount, &ttl,
7882 &nameHasA, &nameHasAAAA, &nameHasSOA ) )
7883 {
7884 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
7885 check( ( addrCount >= 1 ) && ( addrCount <= 255 ) );
7886 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
7887 check( nameHasA || nameHasAAAA );
7888
7889 if( aliasTTLCount > 0 )
7890 {
7891 aliasCount = (uint32_t) aliasTTLCount;
7892 useAliasTTLs = true;
7893 }
7894 nameExists = true;
7895 }
7896 else if( _DNSServerNameIsSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) )
7897 {
7898 nameHasSRV = true;
7899 nameExists = true;
7900 }
7901 require_quiet( nameExists, done );
7902
7903 if( aliasCount > 0 )
7904 {
7905 size_t nameOffset;
7906 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
7907
7908 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7909 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7910 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7911 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7912
7913 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
7914
7915 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7916
7917 nameOffset = kDNSHeaderLength;
7918
7919 for( i = aliasCount; i >= 1; --i )
7920 {
7921 size_t nameLen;
7922 size_t rdataLen;
7923 uint32_t j;
7924 uint32_t aliasTTL;
7925 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
7926
7927 if( nameOffset <= kDNSCompressionOffsetMax )
7928 {
7929 DNSMessageWriteLabelPointer( namePtr, nameOffset );
7930 nameLen = sizeof( namePtr );
7931 }
7932 else
7933 {
7934 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
7935 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
7936 }
7937
7938 if( i >= 2 )
7939 {
7940 char * dst = (char *) &rdataLabel[ 1 ];
7941 char * const lim = (char *) &rdataLabel[ countof( rdataLabel ) ];
7942
7943 if( useAliasTTLs )
7944 {
7945 err = SNPrintF_Add( &dst, lim, kLabelPrefix_AliasTTL );
7946 require_noerr( err, exit );
7947
7948 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
7949 {
7950 err = SNPrintF_Add( &dst, lim, "-%u", aliasTTLs[ j ] );
7951 require_noerr( err, exit );
7952 }
7953 }
7954 else
7955 {
7956 err = SNPrintF_Add( &dst, lim, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
7957 require_noerr( err, exit );
7958 }
7959 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
7960 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
7961 }
7962 else
7963 {
7964 rdataLen = sizeof( superPtr );
7965 }
7966
7967 if( !inForTCP )
7968 {
7969 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
7970
7971 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
7972 {
7973 truncated = true;
7974 goto done;
7975 }
7976 }
7977 ++answerCount;
7978
7979 // Set CNAME record's NAME.
7980
7981 if( nameOffset <= kDNSCompressionOffsetMax )
7982 {
7983 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
7984 require_noerr( err, exit );
7985 }
7986 else
7987 {
7988 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
7989 require_noerr( err, exit );
7990
7991 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
7992 require_noerr( err, exit );
7993 }
7994
7995 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7996
7997 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
7998 dns_fixed_fields_record_init( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
7999 (uint16_t) rdataLen );
8000 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8001 require_noerr( err, exit );
8002
8003 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8004
8005 nameOffset = DataBuffer_GetLen( inDB );
8006
8007 // Set CNAME record's RDATA.
8008
8009 if( i >= 2 )
8010 {
8011 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
8012 require_noerr( err, exit );
8013 }
8014 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8015 require_noerr( err, exit );
8016 }
8017
8018 namePtr[ 0 ] = superPtr[ 0 ];
8019 namePtr[ 1 ] = superPtr[ 1 ];
8020 }
8021 else
8022 {
8023 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8024
8025 DNSMessageWriteLabelPointer( namePtr, kDNSHeaderLength );
8026 }
8027
8028 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
8029 {
8030 uint8_t * lsb; // Pointer to the least significant byte of record data.
8031 size_t recordLen; // Length of the entire record.
8032 size_t rdataLen; // Length of record's RDATA.
8033 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8034 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
8035 const int useBadAddrs = ( me->badUDPMode && !inForTCP ) ? true : false;
8036
8037 if( inQType == kDNSServiceType_A )
8038 {
8039 const uint32_t baseAddrV4 = useBadAddrs ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
8040
8041 require_quiet( nameHasA, done );
8042
8043 rdataLen = 4;
8044 WriteBig32( rdata, baseAddrV4 );
8045 lsb = &rdata[ 3 ];
8046 }
8047 else
8048 {
8049 const uint8_t * const baseAddrV6 = useBadAddrs ? kDNSServerBadBaseAddrV6 : kDNSServerBaseAddrV6;
8050
8051 require_quiet( nameHasAAAA, done );
8052
8053 rdataLen = 16;
8054 memcpy( rdata, baseAddrV6, 16 );
8055 lsb = &rdata[ 15 ];
8056 }
8057
8058 if( randCount > 0 )
8059 {
8060 // Populate the array with all integers between 1 and <randCount>, inclusive.
8061
8062 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
8063
8064 // Prevent dubious static analyzer warning.
8065 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8066 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8067 // for some reason.
8068
8069 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
8070 addrCount, randCount );
8071
8072 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8073 // 1 and <randCount>, inclusive.
8074 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8075 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8076
8077 for( i = 0; i < addrCount; ++i )
8078 {
8079 uint8_t tmp;
8080 uint32_t j;
8081
8082 j = RandomRange( i, randCount - 1 );
8083 if( i != j )
8084 {
8085 tmp = randIntegers[ i ];
8086 randIntegers[ i ] = randIntegers[ j ];
8087 randIntegers[ j ] = tmp;
8088 }
8089 }
8090 }
8091
8092 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8093 for( i = 0; i < addrCount; ++i )
8094 {
8095 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8096 {
8097 truncated = true;
8098 goto done;
8099 }
8100
8101 // Set record NAME.
8102
8103 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8104 require_noerr( err, exit );
8105
8106 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8107
8108 dns_fixed_fields_record_init( &fields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
8109 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8110 require_noerr( err, exit );
8111
8112 // Set record RDATA.
8113
8114 *lsb = ( randCount > 0 ) ? randIntegers[ i ] : ( *lsb + 1 );
8115
8116 err = DataBuffer_Append( inDB, rdata, rdataLen );
8117 require_noerr( err, exit );
8118
8119 ++answerCount;
8120 }
8121 }
8122 else if( inQType == kDNSServiceType_SRV )
8123 {
8124 require_quiet( nameHasSRV, done );
8125
8126 dns_fixed_fields_record_init( &fields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
8127
8128 for( i = 0; i < srvCount; ++i )
8129 {
8130 dns_fixed_fields_srv fieldsSRV;
8131 size_t rdataLen;
8132 size_t recordLen;
8133 const ParsedSRV * const srv = &srvArray[ i ];
8134
8135 rdataLen = sizeof( fieldsSRV ) + srvDomainLen + srv->targetLen + 1;
8136 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8137
8138 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8139 {
8140 truncated = true;
8141 goto done;
8142 }
8143
8144 // Append record NAME.
8145
8146 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8147 require_noerr( err, exit );
8148
8149 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8150
8151 WriteBig16( fields.rdlength, rdataLen );
8152 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8153 require_noerr( err, exit );
8154
8155 // Append SRV RDATA.
8156
8157 dns_fixed_fields_srv_init( &fieldsSRV, srv->priority, srv->weight, srv->port );
8158
8159 err = DataBuffer_Append( inDB, &fieldsSRV, sizeof( fieldsSRV ) );
8160 require_noerr( err, exit );
8161
8162 if( srv->targetLen > 0 )
8163 {
8164 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
8165 require_noerr( err, exit );
8166 }
8167
8168 if( srvDomainLen > 0 )
8169 {
8170 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
8171 require_noerr( err, exit );
8172 }
8173
8174 err = DataBuffer_Append( inDB, "", 1 ); // Append root label.
8175 require_noerr( err, exit );
8176
8177 ++answerCount;
8178 }
8179 }
8180 else if( inQType == kDNSServiceType_SOA )
8181 {
8182 size_t nameLen, recordLen;
8183
8184 require_quiet( nameHasSOA, done );
8185
8186 nameLen = DomainNameLength( me->domain );
8187 if( !inForTCP )
8188 {
8189 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
8190 require_noerr( err, exit );
8191
8192 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8193 {
8194 truncated = true;
8195 goto done;
8196 }
8197 }
8198
8199 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
8200 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
8201 me->defaultTTL, NULL );
8202 require_noerr( err, exit );
8203
8204 ++answerCount;
8205 }
8206
8207 done:
8208 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8209 flags = DNSHeaderGetFlags( hdr );
8210 if( notImplemented )
8211 {
8212 rcode = kDNSRCode_NotImplemented;
8213 }
8214 else
8215 {
8216 flags |= kDNSHeaderFlag_AuthAnswer;
8217 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8218 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8219 }
8220 DNSFlagsSetRCode( flags, rcode );
8221 DNSHeaderSetFlags( hdr, flags );
8222 DNSHeaderSetAnswerCount( hdr, answerCount );
8223 err = kNoErr;
8224
8225 exit:
8226 return( err );
8227 }
8228
8229 static Boolean
8230 _DNSServerNameIsHostname(
8231 DNSServerRef me,
8232 const uint8_t * inName,
8233 uint32_t * outAliasCount,
8234 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
8235 size_t * outAliasTTLCount,
8236 unsigned int * outCount,
8237 unsigned int * outRandCount,
8238 uint32_t * outTTL,
8239 Boolean * outHasA,
8240 Boolean * outHasAAAA,
8241 Boolean * outHasSOA )
8242 {
8243 OSStatus err;
8244 const uint8_t * label;
8245 const uint8_t * nextLabel;
8246 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8247 unsigned int count = 0; // First arg from Count label. Valid values are in [1, 255].
8248 unsigned int randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
8249 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8250 size_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
8251 int hasTagLabel = false;
8252 int hasIPv4Label = false;
8253 int hasIPv6Label = false;
8254 int isNameValid = false;
8255
8256 for( label = inName; label[ 0 ]; label = nextLabel )
8257 {
8258 uint32_t arg;
8259
8260 nextLabel = &label[ 1 + label[ 0 ] ];
8261
8262 // Check if the first label is a valid alias TTL sequence label.
8263
8264 if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_AliasTTL ) == 0 ) )
8265 {
8266 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_AliasTTL ) ];
8267 const char * const end = (const char *) nextLabel;
8268 const char * next;
8269
8270 check( label[ 0 ] <= kDomainLabelLengthMax );
8271
8272 while( ptr < end )
8273 {
8274 if( *ptr != '-' ) break;
8275 ++ptr;
8276 err = DecimalTextToUInt32( ptr, end, &arg, &next );
8277 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8278 inAliasTTLs[ aliasTTLCount++ ] = arg;
8279 ptr = next;
8280 }
8281 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
8282 }
8283
8284 // Check if the first label is a valid alias label.
8285
8286 else if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Alias ) == 0 ) )
8287 {
8288 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Alias ) ];
8289 const char * const end = (const char *) nextLabel;
8290
8291 if( ptr < end )
8292 {
8293 if( *ptr++ != '-' ) break;
8294 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8295 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
8296 aliasCount = arg;
8297 if( ptr != end ) break;
8298 }
8299 else
8300 {
8301 aliasCount = 1;
8302 }
8303 }
8304
8305 // Check if this label is a valid count label.
8306
8307 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Count ) == 0 )
8308 {
8309 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Count ) ];
8310 const char * const end = (const char *) nextLabel;
8311
8312 if( count > 0 ) break; // Count cannot be specified more than once.
8313
8314 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8315 if( err || ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be in [1, 255].
8316 count = (unsigned int) arg;
8317
8318 if( ptr < end )
8319 {
8320 if( *ptr++ != '-' ) break;
8321 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8322 if( err || ( arg < (uint32_t) count ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
8323 randCount = (unsigned int) arg;
8324 if( ptr != end ) break;
8325 }
8326 }
8327
8328 // Check if this label is a valid TTL label.
8329
8330 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_TTL ) == 0 )
8331 {
8332 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_TTL ) ];
8333 const char * const end = (const char *) nextLabel;
8334
8335 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
8336
8337 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8338 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8339 ttl = (int32_t) arg;
8340 if( ptr != end ) break;
8341 }
8342
8343 // Check if this label is a valid IPv4 label.
8344
8345 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv4 ) == 0 )
8346 {
8347 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8348 hasIPv4Label = true;
8349 }
8350
8351 // Check if this label is a valid IPv6 label.
8352
8353 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv6 ) == 0 )
8354 {
8355 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8356 hasIPv6Label = true;
8357 }
8358
8359 // Check if this label is a valid tag label.
8360
8361 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Tag ) == 0 )
8362 {
8363 hasTagLabel = true;
8364 }
8365
8366 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8367 // In both cases, there are no more labels to check.
8368
8369 else
8370 {
8371 if( DomainNameEqual( label, me->domain ) ) isNameValid = true;
8372 break;
8373 }
8374 }
8375 require_quiet( isNameValid, exit );
8376
8377 if( outAliasCount ) *outAliasCount = aliasCount;
8378 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
8379 if( outCount ) *outCount = ( count > 0 ) ? count : 1;
8380 if( outRandCount ) *outRandCount = randCount;
8381 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
8382 if( outHasA ) *outHasA = ( hasIPv4Label || !hasIPv6Label ) ? true : false;
8383 if( outHasAAAA ) *outHasAAAA = ( hasIPv6Label || !hasIPv4Label ) ? true : false;
8384 if( outHasSOA )
8385 {
8386 *outHasSOA = ( !count && ( ttl < 0 ) && !hasIPv4Label && !hasIPv6Label && !hasTagLabel ) ? true : false;
8387 }
8388
8389 exit:
8390 return( isNameValid ? true : false );
8391 }
8392
8393 static Boolean
8394 _DNSServerNameIsSRVName(
8395 DNSServerRef me,
8396 const uint8_t * inName,
8397 const uint8_t ** outDomainPtr,
8398 size_t * outDomainLen,
8399 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
8400 size_t * outSRVCount )
8401 {
8402 OSStatus err;
8403 const uint8_t * label;
8404 const uint8_t * domainPtr;
8405 size_t domainLen;
8406 size_t srvCount;
8407 uint32_t arg;
8408 int isNameValid = false;
8409
8410 label = inName;
8411
8412 // Ensure that first label, i.e, the service label, begins with a '_' character.
8413
8414 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8415 label = DomainNameGetNextLabel( label );
8416
8417 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8418
8419 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8420 label = DomainNameGetNextLabel( label );
8421
8422 // Parse the domain name, if any.
8423
8424 domainPtr = label;
8425 while( *label )
8426 {
8427 if( DomainNameEqual( label, me->domain ) ||
8428 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8429 label = DomainNameGetNextLabel( label );
8430 }
8431 require_quiet( *label, exit );
8432
8433 domainLen = (size_t)( label - domainPtr );
8434
8435 // Parse SRV labels, if any.
8436
8437 srvCount = 0;
8438 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
8439 {
8440 const uint8_t * const nextLabel = DomainNameGetNextLabel( label );
8441 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
8442 const char * const end = (const char *) nextLabel;
8443 const uint8_t * target;
8444 unsigned int priority, weight, port;
8445
8446 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8447 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8448 priority = (unsigned int) arg;
8449
8450 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8451 ++ptr;
8452
8453 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8454 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8455 weight = (unsigned int) arg;
8456
8457 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8458 ++ptr;
8459
8460 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8461 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8462 port = (unsigned int) arg;
8463
8464 require_quiet( ptr == end, exit );
8465
8466 target = nextLabel;
8467 for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
8468 {
8469 if( DomainNameEqual( label, me->domain ) ||
8470 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8471 }
8472 require_quiet( *label, exit );
8473
8474 if( inSRVArray )
8475 {
8476 inSRVArray[ srvCount ].priority = (uint16_t) priority;
8477 inSRVArray[ srvCount ].weight = (uint16_t) weight;
8478 inSRVArray[ srvCount ].port = (uint16_t) port;
8479 inSRVArray[ srvCount ].targetPtr = target;
8480 inSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
8481 }
8482 ++srvCount;
8483 }
8484 require_quiet( DomainNameEqual( label, me->domain ), exit );
8485 isNameValid = true;
8486
8487 if( outDomainPtr ) *outDomainPtr = domainPtr;
8488 if( outDomainLen ) *outDomainLen = domainLen;
8489 if( outSRVCount ) *outSRVCount = srvCount;
8490
8491 exit:
8492 return( isNameValid ? true : false );
8493 }
8494
8495 //===========================================================================================================================
8496 // _DNSServerTCPReadHandler
8497 //===========================================================================================================================
8498
8499 typedef struct
8500 {
8501 DNSServerRef server; // Reference to DNS server object.
8502 sockaddr_ip clientAddr; // Client's address.
8503 dispatch_source_t readSource; // Dispatch read source for client socket.
8504 dispatch_source_t writeSource; // Dispatch write source for client socket.
8505 size_t offset; // Offset into receive buffer.
8506 void * msgPtr; // Pointer to dynamically allocated message buffer.
8507 size_t msgLen; // Length of message buffer.
8508 Boolean readSuspended; // True if the read source is currently suspended.
8509 Boolean writeSuspended; // True if the write source is currently suspended.
8510 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8511 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8512 iovec_t iov[ 2 ]; // IO vector for writing response message.
8513 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8514 int iovCount; // Vector count for SocketWriteData().
8515
8516 } TCPConnectionContext;
8517
8518 static void TCPConnectionStop( TCPConnectionContext *inContext );
8519 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8520 static void TCPConnectionReadHandler( void *inContext );
8521 static void TCPConnectionWriteHandler( void *inContext );
8522
8523 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8524
8525 static void _DNSServerTCPReadHandler( void *inContext )
8526 {
8527 OSStatus err;
8528 SocketContext * const sockCtx = (SocketContext *) inContext;
8529 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
8530 TCPConnectionContext * connection;
8531 socklen_t clientAddrLen;
8532 SocketRef newSock = kInvalidSocketRef;
8533 SocketContext * newSockCtx = NULL;
8534
8535 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8536 require_action( connection, exit, err = kNoMemoryErr );
8537
8538 CFRetain( me );
8539 connection->server = me;
8540
8541 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8542 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8543 err = map_socket_creation_errno( newSock );
8544 require_noerr( err, exit );
8545
8546 err = SocketContextCreate( newSock, connection, &newSockCtx );
8547 require_noerr( err, exit );
8548 newSock = kInvalidSocketRef;
8549
8550 err = DispatchReadSourceCreate( newSockCtx->sock, me->queue, TCPConnectionReadHandler, SocketContextCancelHandler,
8551 newSockCtx, &connection->readSource );
8552 require_noerr( err, exit );
8553 SocketContextRetain( newSockCtx );
8554 dispatch_resume( connection->readSource );
8555
8556 err = DispatchWriteSourceCreate( newSockCtx->sock, me->queue, TCPConnectionWriteHandler, SocketContextCancelHandler,
8557 newSockCtx, &connection->writeSource );
8558 require_noerr( err, exit );
8559 SocketContextRetain( newSockCtx );
8560 connection->writeSuspended = true;
8561 connection = NULL;
8562
8563 exit:
8564 ForgetSocket( &newSock );
8565 SocketContextRelease( newSockCtx );
8566 TCPConnectionForget( &connection );
8567 }
8568
8569 //===========================================================================================================================
8570 // TCPConnectionStop
8571 //===========================================================================================================================
8572
8573 static void TCPConnectionStop( TCPConnectionContext *inContext )
8574 {
8575 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8576 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8577 }
8578
8579 //===========================================================================================================================
8580 // TCPConnectionContextFree
8581 //===========================================================================================================================
8582
8583 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8584 {
8585 check( !inContext->readSource );
8586 check( !inContext->writeSource );
8587 ForgetCF( &inContext->server );
8588 ForgetMem( &inContext->msgPtr );
8589 free( inContext );
8590 }
8591
8592 //===========================================================================================================================
8593 // TCPConnectionReadHandler
8594 //===========================================================================================================================
8595
8596 static void TCPConnectionReadHandler( void *inContext )
8597 {
8598 OSStatus err;
8599 SocketContext * const sockCtx = (SocketContext *) inContext;
8600 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8601 struct timeval now;
8602 uint8_t * responsePtr = NULL; // malloc'd
8603 size_t responseLen;
8604
8605 // Receive message length.
8606
8607 if( !connection->receivedLength )
8608 {
8609 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8610 if( err == EWOULDBLOCK ) goto exit;
8611 require_noerr( err, exit );
8612
8613 connection->offset = 0;
8614 connection->msgLen = ReadBig16( connection->lenBuf );
8615 connection->msgPtr = malloc( connection->msgLen );
8616 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8617 connection->receivedLength = true;
8618 }
8619
8620 // Receive message.
8621
8622 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8623 if( err == EWOULDBLOCK ) goto exit;
8624 require_noerr( err, exit );
8625
8626 gettimeofday( &now, NULL );
8627 dispatch_suspend( connection->readSource );
8628 connection->readSuspended = true;
8629
8630 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8631 connection->msgLen, &connection->clientAddr, &now );
8632
8633 if( connection->msgLen < kDNSHeaderLength )
8634 {
8635 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8636 goto exit;
8637 }
8638
8639 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8640
8641 // Create response.
8642
8643 err = _DNSServerAnswerQueryForTCP( connection->server, connection->msgPtr, connection->msgLen, &responsePtr,
8644 &responseLen );
8645 require_noerr_quiet( err, exit );
8646
8647 // Send response.
8648
8649 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8650
8651 free( connection->msgPtr );
8652 connection->msgPtr = responsePtr;
8653 connection->msgLen = responseLen;
8654 responsePtr = NULL;
8655
8656 check( connection->msgLen <= UINT16_MAX );
8657 WriteBig16( connection->lenBuf, connection->msgLen );
8658 connection->iov[ 0 ].iov_base = connection->lenBuf;
8659 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8660 connection->iov[ 1 ].iov_base = connection->msgPtr;
8661 connection->iov[ 1 ].iov_len = connection->msgLen;
8662
8663 connection->iovPtr = connection->iov;
8664 connection->iovCount = 2;
8665
8666 check( connection->writeSuspended );
8667 dispatch_resume( connection->writeSource );
8668 connection->writeSuspended = false;
8669
8670 exit:
8671 FreeNullSafe( responsePtr );
8672 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8673 }
8674
8675 //===========================================================================================================================
8676 // TCPConnectionWriteHandler
8677 //===========================================================================================================================
8678
8679 static void TCPConnectionWriteHandler( void *inContext )
8680 {
8681 OSStatus err;
8682 SocketContext * const sockCtx = (SocketContext *) inContext;
8683 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8684
8685 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8686 if( err == EWOULDBLOCK ) goto exit;
8687 check_noerr( err );
8688
8689 TCPConnectionForget( &connection );
8690
8691 exit:
8692 return;
8693 }
8694
8695 //===========================================================================================================================
8696 // MDNSReplierCmd
8697 //===========================================================================================================================
8698
8699 typedef struct
8700 {
8701 uint8_t * hostname; // Used as the base name for hostnames and service names.
8702 uint8_t * serviceLabel; // Label containing the base service name.
8703 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
8704 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
8705 size_t bitmapCount; // Number of 64-bit bitmaps.
8706 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
8707 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
8708 uint32_t ifIndex; // Index of the interface to run on.
8709 unsigned int recordCountA; // Number of A records per hostname.
8710 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
8711 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8712 double ucastDropRate; // Probability of dropping a unicast response.
8713 double mcastDropRate; // Probability of dropping a multicast query or response.
8714 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8715 Boolean noAdditionals; // True if responses are to not include additional records.
8716 Boolean useIPv4; // True if the replier is to use IPv4.
8717 Boolean useIPv6; // True if the replier is to use IPv6.
8718 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
8719 #if( TARGET_OS_DARWIN )
8720 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
8721 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
8722 #endif
8723
8724 } MDNSReplierContext;
8725
8726 typedef struct MRResourceRecord MRResourceRecord;
8727 struct MRResourceRecord
8728 {
8729 MRResourceRecord * next; // Next item in list.
8730 uint8_t * name; // Resource record name.
8731 uint16_t type; // Resource record type.
8732 uint16_t class; // Resource record class.
8733 uint32_t ttl; // Resource record TTL.
8734 uint16_t rdlength; // Resource record data length.
8735 uint8_t * rdata; // Resource record data.
8736 const uint8_t * target; // For SRV records, pointer to target in RDATA.
8737 };
8738
8739 typedef struct MRNameOffsetItem MRNameOffsetItem;
8740 struct MRNameOffsetItem
8741 {
8742 MRNameOffsetItem * next; // Next item in list.
8743 uint16_t offset; // Offset of domain name in response message.
8744 uint8_t name[ 1 ]; // Variable-length array for domain name.
8745 };
8746
8747 #if( TARGET_OS_DARWIN )
8748 static void _MDNSReplierFollowedProcessHandler( void *inContext );
8749 #endif
8750 static void _MDNSReplierReadHandler( void *inContext );
8751 static OSStatus
8752 _MDNSReplierAnswerQuery(
8753 MDNSReplierContext * inContext,
8754 const uint8_t * inQueryPtr,
8755 size_t inQueryLen,
8756 sockaddr_ip * inSender,
8757 SocketRef inSock,
8758 unsigned int inIndex );
8759 static OSStatus
8760 _MDNSReplierAnswerListAdd(
8761 MDNSReplierContext * inContext,
8762 MRResourceRecord ** inAnswerList,
8763 unsigned int inIndex,
8764 const uint8_t * inName,
8765 unsigned int inType,
8766 unsigned int inClass );
8767 static void
8768 _MDNSReplierAnswerListRemovePTR(
8769 MRResourceRecord ** inAnswerListPtr,
8770 const uint8_t * inName,
8771 const uint8_t * inRData );
8772 static OSStatus
8773 _MDNSReplierSendOrDropResponse(
8774 MDNSReplierContext * inContext,
8775 MRResourceRecord * inAnswerList,
8776 sockaddr_ip * inQuerier,
8777 SocketRef inSock,
8778 unsigned int inIndex,
8779 Boolean inUnicast );
8780 static OSStatus
8781 _MDNSReplierCreateResponse(
8782 MDNSReplierContext * inContext,
8783 MRResourceRecord * inAnswerList,
8784 unsigned int inIndex,
8785 uint8_t ** outResponsePtr,
8786 size_t * outResponseLen );
8787 static OSStatus
8788 _MDNSReplierAppendNameToResponse(
8789 DataBuffer * inResponse,
8790 const uint8_t * inName,
8791 MRNameOffsetItem ** inNameOffsetListPtr );
8792 static Boolean
8793 _MDNSReplierServiceTypeMatch(
8794 const MDNSReplierContext * inContext,
8795 const uint8_t * inName,
8796 unsigned int * outTXTSize,
8797 unsigned int * outCount );
8798 static Boolean
8799 _MDNSReplierServiceInstanceNameMatch(
8800 const MDNSReplierContext * inContext,
8801 const uint8_t * inName,
8802 unsigned int * outIndex,
8803 unsigned int * outTXTSize,
8804 unsigned int * outCount );
8805 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
8806 static Boolean
8807 _MDNSReplierHostnameMatch(
8808 const MDNSReplierContext * inContext,
8809 const uint8_t * inName,
8810 unsigned int * outIndex );
8811 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
8812 static OSStatus
8813 _MRResourceRecordCreate(
8814 uint8_t * inName,
8815 uint16_t inType,
8816 uint16_t inClass,
8817 uint32_t inTTL,
8818 uint16_t inRDLength,
8819 uint8_t * inRData,
8820 MRResourceRecord ** outRecord );
8821 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
8822 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
8823 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
8824 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
8825 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
8826
8827 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
8828 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8829
8830 static void MDNSReplierCmd( void )
8831 {
8832 OSStatus err;
8833 MDNSReplierContext * context;
8834 SocketRef sockV4 = kInvalidSocketRef;
8835 SocketRef sockV6 = kInvalidSocketRef;
8836 const char * ifname;
8837 size_t len;
8838 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
8839 char ifnameBuf[ IF_NAMESIZE + 1 ];
8840
8841 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
8842 require_noerr_quiet( err, exit );
8843
8844 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
8845 require_noerr_quiet( err, exit );
8846
8847 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
8848 require_noerr_quiet( err, exit );
8849
8850 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
8851 require_noerr_quiet( err, exit );
8852
8853 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
8854 require_noerr_quiet( err, exit );
8855
8856 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
8857 require_noerr_quiet( err, exit );
8858
8859 if( gMDNSReplier_Foreground )
8860 {
8861 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8862 }
8863
8864 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
8865 require_action( context, exit, err = kNoMemoryErr );
8866
8867 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
8868 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
8869 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
8870 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
8871 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
8872 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
8873 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
8874 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
8875 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
8876 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
8877
8878 #if( TARGET_OS_DARWIN )
8879 if( gMDNSReplier_FollowPID )
8880 {
8881 context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
8882 if( err || ( context->followPID < 0 ) )
8883 {
8884 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
8885 goto exit;
8886 }
8887
8888 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
8889 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
8890 require_noerr( err, exit );
8891 dispatch_resume( context->processMonitor );
8892 }
8893 else
8894 {
8895 context->followPID = -1;
8896 }
8897 #endif
8898
8899 if( context->maxDropCount > 0 )
8900 {
8901 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
8902 require_action( context->dropCounters, exit, err = kNoMemoryErr );
8903 }
8904
8905 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
8906 require_action( context->bitmaps, exit, err = kNoMemoryErr );
8907
8908 // Create the base hostname label.
8909
8910 len = strlen( gMDNSReplier_Hostname );
8911 if( context->maxInstanceCount > 1 )
8912 {
8913 unsigned int maxInstanceCount, digitCount;
8914
8915 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8916 // "-<instance index>" to the base hostname.
8917
8918 maxInstanceCount = context->maxInstanceCount;
8919 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
8920 len += ( 3 + digitCount );
8921 }
8922
8923 if( len <= kDomainLabelLengthMax )
8924 {
8925 uint8_t * dst = &name[ 1 ];
8926 uint8_t * lim = &name[ countof( name ) ];
8927
8928 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
8929 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8930
8931 err = DomainNameDupLower( name, &context->hostname, NULL );
8932 require_noerr( err, exit );
8933 }
8934 else
8935 {
8936 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8937 gMDNSReplier_Hostname, context->maxInstanceCount );
8938 goto exit;
8939 }
8940
8941 // Create the service label.
8942
8943 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
8944 if( len <= kDomainLabelLengthMax )
8945 {
8946 uint8_t * dst = &name[ 1 ];
8947 uint8_t * lim = &name[ countof( name ) ];
8948
8949 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
8950 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8951
8952 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
8953 require_noerr( err, exit );
8954 }
8955 else
8956 {
8957 FPrintF( stderr, "error: Service type tag is too long.\n" );
8958 goto exit;
8959 }
8960
8961 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
8962 require_noerr_quiet( err, exit );
8963
8964 ifname = if_indextoname( context->ifIndex, ifnameBuf );
8965 require_action( ifname, exit, err = kNameErr );
8966
8967 // Set up IPv4 socket.
8968
8969 if( context->useIPv4 )
8970 {
8971 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
8972 require_noerr( err, exit );
8973 }
8974
8975 // Set up IPv6 socket.
8976
8977 if( context->useIPv6 )
8978 {
8979 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
8980 require_noerr( err, exit );
8981 }
8982
8983 // Create dispatch read sources for socket(s).
8984
8985 if( IsValidSocket( sockV4 ) )
8986 {
8987 SocketContext * sockCtx;
8988
8989 err = SocketContextCreate( sockV4, context, &sockCtx );
8990 require_noerr( err, exit );
8991 sockV4 = kInvalidSocketRef;
8992
8993 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
8994 &context->readSourceV4 );
8995 if( err ) ForgetSocketContext( &sockCtx );
8996 require_noerr( err, exit );
8997
8998 dispatch_resume( context->readSourceV4 );
8999 }
9000
9001 if( IsValidSocket( sockV6 ) )
9002 {
9003 SocketContext * sockCtx;
9004
9005 err = SocketContextCreate( sockV6, context, &sockCtx );
9006 require_noerr( err, exit );
9007 sockV6 = kInvalidSocketRef;
9008
9009 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9010 &context->readSourceV6 );
9011 if( err ) ForgetSocketContext( &sockCtx );
9012 require_noerr( err, exit );
9013
9014 dispatch_resume( context->readSourceV6 );
9015 }
9016
9017 dispatch_main();
9018
9019 exit:
9020 ForgetSocket( &sockV4 );
9021 ForgetSocket( &sockV6 );
9022 exit( 1 );
9023 }
9024
9025 #if( TARGET_OS_DARWIN )
9026 //===========================================================================================================================
9027 // _MDNSReplierFollowedProcessHandler
9028 //===========================================================================================================================
9029
9030 static void _MDNSReplierFollowedProcessHandler( void *inContext )
9031 {
9032 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
9033
9034 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
9035 {
9036 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
9037 exit( 0 );
9038 }
9039 }
9040 #endif
9041
9042 //===========================================================================================================================
9043 // _MDNSReplierReadHandler
9044 //===========================================================================================================================
9045
9046 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9047
9048 static void _MDNSReplierReadHandler( void *inContext )
9049 {
9050 OSStatus err;
9051 SocketContext * const sockCtx = (SocketContext *) inContext;
9052 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
9053 size_t msgLen;
9054 sockaddr_ip sender;
9055 const DNSHeader * hdr;
9056 unsigned int flags, questionCount, i, j;
9057 const uint8_t * ptr;
9058 int drop, isMetaQuery;
9059
9060 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
9061 NULL, NULL, NULL, NULL );
9062 require_noerr( err, exit );
9063
9064 if( msgLen < kDNSHeaderLength )
9065 {
9066 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
9067 goto exit;
9068 }
9069
9070 // Perform header field checks.
9071 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9072
9073 hdr = (DNSHeader *) context->msgBuf;
9074 flags = DNSHeaderGetFlags( hdr );
9075 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
9076 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
9077 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
9078
9079 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
9080
9081 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9082 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
9083
9084 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9085
9086 questionCount = DNSHeaderGetQuestionCount( hdr );
9087 require_quiet( questionCount > 0, exit );
9088
9089 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
9090
9091 isMetaQuery = false;
9092 ptr = (const uint8_t *) &hdr[ 1 ];
9093 for( i = 0; i < questionCount; ++i )
9094 {
9095 unsigned int count, index;
9096 uint16_t qtype, qclass;
9097 uint8_t qname[ kDomainNameLengthMax ];
9098
9099 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
9100 require_noerr_quiet( err, exit );
9101
9102 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
9103
9104 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
9105 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
9106 {
9107 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
9108 {
9109 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
9110 }
9111 }
9112 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
9113 {
9114 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
9115 {
9116 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
9117 {
9118 if( count < 64 )
9119 {
9120 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
9121 break;
9122 }
9123 else
9124 {
9125 context->bitmaps[ j ] = ~UINT64_C( 0 );
9126 count -= 64;
9127 }
9128 }
9129 }
9130 }
9131 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
9132 {
9133 isMetaQuery = true;
9134 }
9135 }
9136
9137 // Attempt to answer the query message using selected record sets.
9138
9139 if( isMetaQuery )
9140 {
9141 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
9142 check_noerr( err );
9143 }
9144 if( drop ) goto exit;
9145
9146 for( i = 0; i < context->bitmapCount; ++i )
9147 {
9148 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
9149 {
9150 const uint64_t bitmask = UINT64_C( 1 ) << j;
9151
9152 if( context->bitmaps[ i ] & bitmask )
9153 {
9154 context->bitmaps[ i ] &= ~bitmask;
9155
9156 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
9157 ( i * 64 ) + j + 1 );
9158 check_noerr( err );
9159 }
9160 }
9161 }
9162
9163 exit:
9164 return;
9165 }
9166
9167 //===========================================================================================================================
9168 // _MDNSReplierAnswerQuery
9169 //===========================================================================================================================
9170
9171 static OSStatus
9172 _MDNSReplierAnswerQuery(
9173 MDNSReplierContext * inContext,
9174 const uint8_t * inQueryPtr,
9175 size_t inQueryLen,
9176 sockaddr_ip * inSender,
9177 SocketRef inSock,
9178 unsigned int inIndex )
9179 {
9180 OSStatus err;
9181 const DNSHeader * hdr;
9182 const uint8_t * ptr;
9183 unsigned int questionCount, answerCount, i;
9184 MRResourceRecord * ucastAnswerList = NULL;
9185 MRResourceRecord * mcastAnswerList = NULL;
9186
9187 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9188
9189 // Get answers for questions.
9190
9191 check( inQueryLen >= kDNSHeaderLength );
9192 hdr = (const DNSHeader *) inQueryPtr;
9193 questionCount = DNSHeaderGetQuestionCount( hdr );
9194
9195 ptr = (const uint8_t *) &hdr[ 1 ];
9196 for( i = 0; i < questionCount; ++i )
9197 {
9198 MRResourceRecord ** answerListPtr;
9199 uint16_t qtype, qclass;
9200 uint8_t qname[ kDomainNameLengthMax ];
9201
9202 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
9203 require_noerr_quiet( err, exit );
9204
9205 if( qclass & kQClassUnicastResponseBit )
9206 {
9207 qclass &= ~kQClassUnicastResponseBit;
9208 answerListPtr = &ucastAnswerList;
9209 }
9210 else
9211 {
9212 answerListPtr = &mcastAnswerList;
9213 }
9214
9215 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
9216 require_noerr( err, exit );
9217 }
9218 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9219
9220 // Suppress known answers.
9221 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9222 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9223
9224 answerCount = DNSHeaderGetAnswerCount( hdr );
9225 for( i = 0; i < answerCount; ++i )
9226 {
9227 const uint8_t * rdataPtr;
9228 const uint8_t * recordPtr;
9229 uint16_t type, class;
9230 uint8_t name[ kDomainNameLengthMax ];
9231 uint8_t instance[ kDomainNameLengthMax ];
9232
9233 recordPtr = ptr;
9234 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
9235 require_noerr_quiet( err, exit );
9236
9237 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
9238
9239 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
9240 require_noerr( err, exit );
9241
9242 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
9243 require_noerr_quiet( err, exit );
9244
9245 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
9246 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
9247 }
9248 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9249
9250 // Send or drop responses.
9251
9252 if( ucastAnswerList )
9253 {
9254 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
9255 require_noerr( err, exit );
9256 }
9257
9258 if( mcastAnswerList )
9259 {
9260 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
9261 require_noerr( err, exit );
9262 }
9263 err = kNoErr;
9264
9265 exit:
9266 _MRResourceRecordFreeList( ucastAnswerList );
9267 _MRResourceRecordFreeList( mcastAnswerList );
9268 return( err );
9269 }
9270
9271 //===========================================================================================================================
9272 // _MDNSReplierAnswerListAdd
9273 //===========================================================================================================================
9274
9275 static OSStatus
9276 _MDNSReplierAnswerListAdd(
9277 MDNSReplierContext * inContext,
9278 MRResourceRecord ** inAnswerList,
9279 unsigned int inIndex,
9280 const uint8_t * inName,
9281 unsigned int inType,
9282 unsigned int inClass )
9283 {
9284 OSStatus err;
9285 uint8_t * recordName = NULL;
9286 uint8_t * rdataPtr = NULL;
9287 size_t rdataLen;
9288 MRResourceRecord * answer;
9289 MRResourceRecord ** answerPtr;
9290 const uint8_t * const hostname = inContext->hostname;
9291 unsigned int i;
9292 uint32_t index;
9293 unsigned int count, txtSize;
9294
9295 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9296 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
9297
9298 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9299 {
9300 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
9301 {
9302 err = kNoErr;
9303 goto exit;
9304 }
9305 }
9306
9307 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9308 // instance count are for answering queries about service instances.
9309
9310 if( inIndex == 0 )
9311 {
9312 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
9313 {
9314 int listHasTXT = false;
9315
9316 if( inType == kDNSServiceType_ANY )
9317 {
9318 for( answer = *inAnswerList; answer; answer = answer->next )
9319 {
9320 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
9321 {
9322 listHasTXT = true;
9323 break;
9324 }
9325 }
9326 }
9327
9328 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9329 {
9330 err = DomainNameDupLower( inName, &recordName, NULL );
9331 require_noerr( err, exit );
9332
9333 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
9334 require_noerr( err, exit );
9335
9336 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9337 (uint16_t) rdataLen, rdataPtr, &answer );
9338 require_noerr( err, exit );
9339 recordName = NULL;
9340 rdataPtr = NULL;
9341
9342 *answerPtr = answer;
9343 }
9344 else if( inType == kDNSServiceType_NSEC )
9345 {
9346 err = DomainNameDupLower( inName, &recordName, NULL );
9347 require_noerr( err, exit );
9348
9349 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
9350 require_noerr( err, exit );
9351
9352 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9353 (uint16_t) rdataLen, rdataPtr, &answer );
9354 require_noerr( err, exit );
9355 recordName = NULL;
9356 rdataPtr = NULL;
9357
9358 *answerPtr = answer;
9359 }
9360 }
9361 }
9362 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
9363 {
9364 int listHasA = false;
9365 int listHasAAAA = false;
9366
9367 if( inType == kDNSServiceType_ANY )
9368 {
9369 for( answer = *inAnswerList; answer; answer = answer->next )
9370 {
9371 if( answer->type == kDNSServiceType_A )
9372 {
9373 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
9374 }
9375 else if( answer->type == kDNSServiceType_AAAA )
9376 {
9377 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
9378 }
9379 if( listHasA && listHasAAAA ) break;
9380 }
9381 }
9382
9383 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
9384 {
9385 for( i = 1; i <= inContext->recordCountA; ++i )
9386 {
9387 err = DomainNameDupLower( inName, &recordName, NULL );
9388 require_noerr( err, exit );
9389
9390 rdataLen = 4;
9391 rdataPtr = (uint8_t *) malloc( rdataLen );
9392 require_action( rdataPtr, exit, err = kNoMemoryErr );
9393
9394 rdataPtr[ 0 ] = 0;
9395 WriteBig16( &rdataPtr[ 1 ], inIndex );
9396 rdataPtr[ 3 ] = (uint8_t) i;
9397
9398 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9399 (uint16_t) rdataLen, rdataPtr, &answer );
9400 require_noerr( err, exit );
9401 recordName = NULL;
9402 rdataPtr = NULL;
9403
9404 *answerPtr = answer;
9405 answerPtr = &answer->next;
9406 }
9407 }
9408
9409 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
9410 {
9411 for( i = 1; i <= inContext->recordCountAAAA; ++i )
9412 {
9413 err = DomainNameDupLower( inName, &recordName, NULL );
9414 require_noerr( err, exit );
9415
9416 rdataLen = 16;
9417 rdataPtr = (uint8_t *) _memdup( kMDNSReplierBaseAddrV6, rdataLen );
9418 require_action( rdataPtr, exit, err = kNoMemoryErr );
9419
9420 WriteBig16( &rdataPtr[ 12 ], inIndex );
9421 rdataPtr[ 15 ] = (uint8_t) i;
9422
9423 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9424 (uint16_t) rdataLen, rdataPtr, &answer );
9425 require_noerr( err, exit );
9426 recordName = NULL;
9427 rdataPtr = NULL;
9428
9429 *answerPtr = answer;
9430 answerPtr = &answer->next;
9431 }
9432 }
9433 else if( inType == kDNSServiceType_NSEC )
9434 {
9435 err = DomainNameDupLower( inName, &recordName, NULL );
9436 require_noerr( err, exit );
9437
9438 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
9439 {
9440 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
9441 require_noerr( err, exit );
9442 }
9443 else if( inContext->recordCountA > 0 )
9444 {
9445 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
9446 require_noerr( err, exit );
9447 }
9448 else if( inContext->recordCountAAAA > 0 )
9449 {
9450 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
9451 require_noerr( err, exit );
9452 }
9453 else
9454 {
9455 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
9456 require_noerr( err, exit );
9457 }
9458
9459 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9460 (uint16_t) rdataLen, rdataPtr, &answer );
9461 require_noerr( err, exit );
9462 recordName = NULL;
9463 rdataPtr = NULL;
9464
9465 *answerPtr = answer;
9466 }
9467 }
9468 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
9469 {
9470 int listHasPTR = false;
9471
9472 if( inType == kDNSServiceType_ANY )
9473 {
9474 for( answer = *inAnswerList; answer; answer = answer->next )
9475 {
9476 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
9477 {
9478 listHasPTR = true;
9479 break;
9480 }
9481 }
9482 }
9483
9484 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
9485 {
9486 size_t recordNameLen;
9487 uint8_t * ptr;
9488 uint8_t * lim;
9489
9490 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
9491 require_noerr( err, exit );
9492
9493 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
9494 rdataPtr = (uint8_t *) malloc( rdataLen );
9495 require_action( rdataPtr, exit, err = kNoMemoryErr );
9496
9497 lim = &rdataPtr[ rdataLen ];
9498
9499 ptr = &rdataPtr[ 1 ];
9500 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9501 ptr += hostname[ 0 ];
9502 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
9503 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
9504
9505 check( (size_t)( lim - ptr ) >= recordNameLen );
9506 memcpy( ptr, recordName, recordNameLen );
9507 ptr += recordNameLen;
9508
9509 rdataLen = (size_t)( ptr - rdataPtr );
9510
9511 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9512 (uint16_t) rdataLen, rdataPtr, &answer );
9513 require_noerr( err, exit );
9514 recordName = NULL;
9515 rdataPtr = NULL;
9516
9517 *answerPtr = answer;
9518 }
9519 }
9520 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
9521 ( index == inIndex ) && ( count >= inIndex ) )
9522 {
9523 int listHasSRV = false;
9524 int listHasTXT = false;
9525
9526 if( inType == kDNSServiceType_ANY )
9527 {
9528 for( answer = *inAnswerList; answer; answer = answer->next )
9529 {
9530 if( answer->type == kDNSServiceType_SRV )
9531 {
9532 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
9533 }
9534 else if( answer->type == kDNSServiceType_TXT )
9535 {
9536 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
9537 }
9538 if( listHasSRV && listHasTXT ) break;
9539 }
9540 }
9541
9542 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
9543 {
9544 dns_fixed_fields_srv * fields;
9545 uint8_t * ptr;
9546 uint8_t * lim;
9547 uint8_t * targetPtr;
9548
9549 err = DomainNameDupLower( inName, &recordName, NULL );
9550 require_noerr( err, exit );
9551
9552 rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
9553 rdataPtr = (uint8_t *) malloc( rdataLen );
9554 require_action( rdataPtr, exit, err = kNoMemoryErr );
9555
9556 lim = &rdataPtr[ rdataLen ];
9557
9558 fields = (dns_fixed_fields_srv *) rdataPtr;
9559 dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
9560
9561 targetPtr = (uint8_t *) &fields[ 1 ];
9562
9563 ptr = &targetPtr[ 1 ];
9564 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9565 ptr += hostname[ 0 ];
9566 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
9567 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
9568
9569 check( (size_t)( lim - ptr ) >= kLocalNameLen );
9570 memcpy( ptr, kLocalName, kLocalNameLen );
9571 ptr += kLocalNameLen;
9572
9573 rdataLen = (size_t)( ptr - rdataPtr );
9574
9575 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9576 (uint16_t) rdataLen, rdataPtr, &answer );
9577 require_noerr( err, exit );
9578 recordName = NULL;
9579 rdataPtr = NULL;
9580
9581 *answerPtr = answer;
9582 answerPtr = &answer->next;
9583 }
9584
9585 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9586 {
9587 err = DomainNameDupLower( inName, &recordName, NULL );
9588 require_noerr( err, exit );
9589
9590 rdataLen = txtSize;
9591 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
9592 require_noerr( err, exit );
9593
9594 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9595 (uint16_t) rdataLen, rdataPtr, &answer );
9596 require_noerr( err, exit );
9597 recordName = NULL;
9598 rdataPtr = NULL;
9599
9600 *answerPtr = answer;
9601 }
9602 else if( inType == kDNSServiceType_NSEC )
9603 {
9604 err = DomainNameDupLower( inName, &recordName, NULL );
9605 require_noerr( err, exit );
9606
9607 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
9608 require_noerr( err, exit );
9609
9610 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9611 (uint16_t) rdataLen, rdataPtr, &answer );
9612 require_noerr( err, exit );
9613 recordName = NULL;
9614 rdataPtr = NULL;
9615
9616 *answerPtr = answer;
9617 }
9618 }
9619 err = kNoErr;
9620
9621 exit:
9622 FreeNullSafe( recordName );
9623 FreeNullSafe( rdataPtr );
9624 return( err );
9625 }
9626
9627 //===========================================================================================================================
9628 // _MDNSReplierAnswerListRemovePTR
9629 //===========================================================================================================================
9630
9631 static void
9632 _MDNSReplierAnswerListRemovePTR(
9633 MRResourceRecord ** inAnswerListPtr,
9634 const uint8_t * inName,
9635 const uint8_t * inRData )
9636 {
9637 MRResourceRecord * answer;
9638 MRResourceRecord ** answerPtr;
9639
9640 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9641 {
9642 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
9643 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
9644 }
9645 if( answer )
9646 {
9647 *answerPtr = answer->next;
9648 _MRResourceRecordFree( answer );
9649 }
9650 }
9651
9652 //===========================================================================================================================
9653 // _MDNSReplierSendOrDropResponse
9654 //===========================================================================================================================
9655
9656 static OSStatus
9657 _MDNSReplierSendOrDropResponse(
9658 MDNSReplierContext * inContext,
9659 MRResourceRecord * inAnswerList,
9660 sockaddr_ip * inQuerier,
9661 SocketRef inSock,
9662 unsigned int inIndex,
9663 Boolean inUnicast )
9664 {
9665 OSStatus err;
9666 uint8_t * responsePtr = NULL;
9667 size_t responseLen;
9668 const struct sockaddr * destAddr;
9669 ssize_t n;
9670 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
9671 int drop;
9672
9673 check( inIndex <= inContext->maxInstanceCount );
9674
9675 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9676 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9677 // as opposed to those for service instance records, are never dropped.
9678
9679 drop = false;
9680 if( inIndex > 0 )
9681 {
9682 if( inContext->maxDropCount > 0 )
9683 {
9684 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
9685
9686 if( *dropCount < inContext->maxDropCount )
9687 {
9688 if( ShouldDrop( dropRate ) ) drop = true;
9689 *dropCount += 1;
9690 }
9691 }
9692 else if( ShouldDrop( dropRate ) )
9693 {
9694 drop = true;
9695 }
9696 }
9697
9698 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
9699 require_noerr( err, exit );
9700
9701 if( inUnicast )
9702 {
9703 destAddr = &inQuerier->sa;
9704 }
9705 else
9706 {
9707 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9708 }
9709
9710 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9711 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
9712
9713 if( !drop )
9714 {
9715 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
9716 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
9717 require_noerr( err, exit );
9718 }
9719
9720 exit:
9721 FreeNullSafe( responsePtr );
9722 return( err );
9723 }
9724
9725 //===========================================================================================================================
9726 // _MDNSReplierCreateResponse
9727 //===========================================================================================================================
9728
9729 static OSStatus
9730 _MDNSReplierCreateResponse(
9731 MDNSReplierContext * inContext,
9732 MRResourceRecord * inAnswerList,
9733 unsigned int inIndex,
9734 uint8_t ** outResponsePtr,
9735 size_t * outResponseLen )
9736 {
9737 OSStatus err;
9738 DataBuffer responseDB;
9739 DNSHeader hdr;
9740 MRResourceRecord * answer;
9741 uint8_t * responsePtr;
9742 size_t responseLen, len;
9743 unsigned int answerCount, recordCount;
9744 MRNameOffsetItem * nameOffsetList = NULL;
9745
9746 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
9747
9748 // The current answers in the answer list will make up the response's Answer Record Section.
9749
9750 answerCount = 0;
9751 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
9752
9753 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9754
9755 if( !inContext->noAdditionals )
9756 {
9757 for( answer = inAnswerList; answer; answer = answer->next )
9758 {
9759 switch( answer->type )
9760 {
9761 case kDNSServiceType_PTR:
9762 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
9763 answer->class );
9764 require_noerr( err, exit );
9765
9766 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
9767 answer->class );
9768 require_noerr( err, exit );
9769 break;
9770
9771 case kDNSServiceType_SRV:
9772 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
9773 answer->class );
9774 require_noerr( err, exit );
9775
9776 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
9777 answer->class );
9778 require_noerr( err, exit );
9779
9780 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9781 answer->class );
9782 require_noerr( err, exit );
9783 break;
9784
9785 case kDNSServiceType_TXT:
9786 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9787 answer->class );
9788 require_noerr( err, exit );
9789 break;
9790
9791 case kDNSServiceType_A:
9792 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
9793 answer->class );
9794 require_noerr( err, exit );
9795
9796 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9797 answer->class );
9798 require_noerr( err, exit );
9799 break;
9800
9801 case kDNSServiceType_AAAA:
9802 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
9803 answer->class );
9804 require_noerr( err, exit );
9805
9806 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9807 answer->class );
9808 require_noerr( err, exit );
9809 break;
9810
9811 default:
9812 break;
9813 }
9814 }
9815 }
9816
9817 // Append a provisional header to the response message.
9818
9819 memset( &hdr, 0, sizeof( hdr ) );
9820 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
9821
9822 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
9823 require_noerr( err, exit );
9824
9825 // Append answers to response message.
9826
9827 responseLen = DataBuffer_GetLen( &responseDB );
9828 recordCount = 0;
9829 for( answer = inAnswerList; answer; answer = answer->next )
9830 {
9831 dns_fixed_fields_record fields;
9832 unsigned int class;
9833
9834 // Append record NAME.
9835
9836 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
9837 require_noerr( err, exit );
9838
9839 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9840
9841 class = answer->class;
9842 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
9843 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
9844 ( answer->type == kDNSServiceType_NSEC ) )
9845 {
9846 class |= kRRClassCacheFlushBit;
9847 }
9848
9849 dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
9850 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
9851 require_noerr( err, exit );
9852
9853 // Append record RDATA.
9854 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9855
9856 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
9857 ( answer->type == kDNSServiceType_NSEC ) )
9858 {
9859 size_t rdlength;
9860 uint8_t * rdLengthPtr;
9861 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
9862 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
9863
9864 if( answer->type == kDNSServiceType_PTR )
9865 {
9866 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9867 require_noerr( err, exit );
9868 }
9869 else if( answer->type == kDNSServiceType_SRV )
9870 {
9871 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
9872
9873 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
9874 require_noerr( err, exit );
9875
9876 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
9877 require_noerr( err, exit );
9878 }
9879 else
9880 {
9881 const size_t nameLen = DomainNameLength( answer->rdata );
9882
9883 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9884 require_noerr( err, exit );
9885
9886 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
9887
9888 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
9889 require_noerr( err, exit );
9890 }
9891
9892 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9893
9894 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
9895 check( rdlength <= UINT16_MAX );
9896
9897 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
9898 WriteBig16( rdLengthPtr, rdlength );
9899 }
9900 else
9901 {
9902 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
9903 require_noerr( err, exit );
9904 }
9905
9906 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
9907 responseLen = DataBuffer_GetLen( &responseDB );
9908 ++recordCount;
9909 }
9910
9911 // Set the response header's Answer and Additional record counts.
9912 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9913 // response message to exceed the maximum mDNS message size.
9914
9915 if( recordCount <= answerCount )
9916 {
9917 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
9918 }
9919 else
9920 {
9921 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
9922 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
9923 }
9924
9925 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
9926 require_noerr( err, exit );
9927
9928 if( outResponsePtr ) *outResponsePtr = responsePtr;
9929 if( outResponseLen ) *outResponseLen = responseLen;
9930
9931 exit:
9932 _MRNameOffsetItemFreeList( nameOffsetList );
9933 DataBuffer_Free( &responseDB );
9934 return( err );
9935 }
9936
9937 //===========================================================================================================================
9938 // _MDNSReplierAppendNameToResponse
9939 //===========================================================================================================================
9940
9941 static OSStatus
9942 _MDNSReplierAppendNameToResponse(
9943 DataBuffer * inResponse,
9944 const uint8_t * inName,
9945 MRNameOffsetItem ** inNameOffsetListPtr )
9946 {
9947 OSStatus err;
9948 const uint8_t * subname;
9949 const uint8_t * limit;
9950 size_t nameOffset;
9951 MRNameOffsetItem * item;
9952 uint8_t compressionPtr[ 2 ];
9953
9954 nameOffset = DataBuffer_GetLen( inResponse );
9955
9956 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9957
9958 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
9959 {
9960 for( item = *inNameOffsetListPtr; item; item = item->next )
9961 {
9962 if( DomainNameEqual( item->name, subname ) ) break;
9963 }
9964
9965 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9966 // the subname's first label.
9967
9968 if( item )
9969 {
9970 DNSMessageWriteLabelPointer( compressionPtr, item->offset );
9971
9972 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
9973 require_noerr( err, exit );
9974 break;
9975 }
9976 else
9977 {
9978 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
9979 require_noerr( err, exit );
9980 }
9981 }
9982
9983 // 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
9984 // label were appended to the response message, so a root label is needed to terminate the complete name.
9985
9986 if( subname[ 0 ] == 0 )
9987 {
9988 err = DataBuffer_Append( inResponse, "", 1 );
9989 require_noerr( err, exit );
9990 }
9991
9992 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
9993
9994 limit = subname;
9995 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
9996 {
9997 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
9998
9999 if( subnameOffset > kDNSCompressionOffsetMax ) break;
10000
10001 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
10002 require_noerr( err, exit );
10003
10004 item->next = *inNameOffsetListPtr;
10005 *inNameOffsetListPtr = item;
10006 }
10007 err = kNoErr;
10008
10009 exit:
10010 return( err );
10011 }
10012
10013 //===========================================================================================================================
10014 // _MDNSReplierServiceTypeMatch
10015 //===========================================================================================================================
10016
10017 static Boolean
10018 _MDNSReplierServiceTypeMatch(
10019 const MDNSReplierContext * inContext,
10020 const uint8_t * inName,
10021 unsigned int * outTXTSize,
10022 unsigned int * outCount )
10023 {
10024 OSStatus err;
10025 const char * ptr;
10026 const char * end;
10027 uint32_t txtSize, count;
10028 const uint8_t * const serviceLabel = inContext->serviceLabel;
10029 int nameMatches = false;
10030
10031 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
10032 if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
10033
10034 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
10035 end = (const char *) &inName[ 1 + inName[ 0 ] ];
10036
10037 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10038 ++ptr;
10039
10040 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
10041 require_noerr_quiet( err, exit );
10042 require_quiet( txtSize <= UINT16_MAX, exit );
10043
10044 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10045 ++ptr;
10046
10047 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
10048 require_noerr_quiet( err, exit );
10049 require_quiet( count <= UINT16_MAX, exit );
10050 require_quiet( ptr == end, exit );
10051
10052 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
10053 nameMatches = true;
10054
10055 if( outTXTSize ) *outTXTSize = txtSize;
10056 if( outCount ) *outCount = count;
10057
10058 exit:
10059 return( nameMatches ? true : false );
10060 }
10061
10062 //===========================================================================================================================
10063 // _MDNSReplierServiceInstanceNameMatch
10064 //===========================================================================================================================
10065
10066 static Boolean
10067 _MDNSReplierServiceInstanceNameMatch(
10068 const MDNSReplierContext * inContext,
10069 const uint8_t * inName,
10070 unsigned int * outIndex,
10071 unsigned int * outTXTSize,
10072 unsigned int * outCount )
10073 {
10074 OSStatus err;
10075 const uint8_t * ptr;
10076 const uint8_t * end;
10077 uint32_t index;
10078 unsigned int txtSize, count;
10079 const uint8_t * const hostname = inContext->hostname;
10080 int nameMatches = false;
10081
10082 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10083 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10084
10085 ptr = &inName[ 1 + hostname[ 0 ] ];
10086 end = &inName[ 1 + inName[ 0 ] ];
10087 if( ptr < end )
10088 {
10089 require_quiet( ( end - ptr ) >= 2, exit );
10090 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
10091 ptr += 2;
10092
10093 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10094 require_noerr_quiet( err, exit );
10095 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10096
10097 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
10098 ++ptr;
10099 }
10100 else
10101 {
10102 index = 1;
10103 }
10104
10105 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
10106 nameMatches = true;
10107
10108 if( outIndex ) *outIndex = index;
10109 if( outTXTSize ) *outTXTSize = txtSize;
10110 if( outCount ) *outCount = count;
10111
10112 exit:
10113 return( nameMatches ? true : false );
10114 }
10115
10116 //===========================================================================================================================
10117 // _MDNSReplierAboutRecordNameMatch
10118 //===========================================================================================================================
10119
10120 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10121 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10122
10123 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
10124 {
10125 const uint8_t * subname;
10126 const uint8_t * const hostname = inContext->hostname;
10127 int nameMatches = false;
10128
10129 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
10130 subname = DomainNameGetNextLabel( inName );
10131
10132 if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
10133 subname = DomainNameGetNextLabel( subname );
10134
10135 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
10136 nameMatches = true;
10137
10138 exit:
10139 return( nameMatches ? true : false );
10140 }
10141
10142 //===========================================================================================================================
10143 // _MDNSReplierHostnameMatch
10144 //===========================================================================================================================
10145
10146 static Boolean
10147 _MDNSReplierHostnameMatch(
10148 const MDNSReplierContext * inContext,
10149 const uint8_t * inName,
10150 unsigned int * outIndex )
10151 {
10152 OSStatus err;
10153 const uint8_t * ptr;
10154 const uint8_t * end;
10155 uint32_t index;
10156 const uint8_t * const hostname = inContext->hostname;
10157 int nameMatches = false;
10158
10159 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10160 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10161
10162 ptr = &inName[ 1 + hostname[ 0 ] ];
10163 end = &inName[ 1 + inName[ 0 ] ];
10164 if( ptr < end )
10165 {
10166 require_quiet( *ptr == '-', exit );
10167 ++ptr;
10168
10169 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10170 require_noerr_quiet( err, exit );
10171 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10172 require_quiet( ptr == end, exit );
10173 }
10174 else
10175 {
10176 index = 1;
10177 }
10178
10179 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
10180 nameMatches = true;
10181
10182 if( outIndex ) *outIndex = index;
10183
10184 exit:
10185 return( nameMatches ? true : false );
10186 }
10187
10188 //===========================================================================================================================
10189 // _MDNSReplierCreateTXTRecord
10190 //===========================================================================================================================
10191
10192 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
10193 {
10194 OSStatus err;
10195 uint8_t * txt;
10196 uint8_t * ptr;
10197 size_t i, wholeCount, remCount;
10198 uint32_t hash;
10199 int n;
10200 uint8_t txtStr[ 16 ];
10201
10202 require_action_quiet( inSize > 0, exit, err = kSizeErr );
10203
10204 txt = (uint8_t *) malloc( inSize );
10205 require_action( txt, exit, err = kNoMemoryErr );
10206
10207 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
10208
10209 txtStr[ 0 ] = 15;
10210 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
10211 check( n == 15 );
10212
10213 ptr = txt;
10214 wholeCount = inSize / 16;
10215 for( i = 0; i < wholeCount; ++i )
10216 {
10217 memcpy( ptr, txtStr, 16 );
10218 ptr += 16;
10219 }
10220
10221 remCount = inSize % 16;
10222 if( remCount > 0 )
10223 {
10224 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
10225 memcpy( ptr, txtStr, remCount );
10226 ptr += remCount;
10227 }
10228 check( ptr == &txt[ inSize ] );
10229
10230 *outTXT = txt;
10231 err = kNoErr;
10232
10233 exit:
10234 return( err );
10235 }
10236
10237 //===========================================================================================================================
10238 // _MRResourceRecordCreate
10239 //===========================================================================================================================
10240
10241 static OSStatus
10242 _MRResourceRecordCreate(
10243 uint8_t * inName,
10244 uint16_t inType,
10245 uint16_t inClass,
10246 uint32_t inTTL,
10247 uint16_t inRDLength,
10248 uint8_t * inRData,
10249 MRResourceRecord ** outRecord )
10250 {
10251 OSStatus err;
10252 MRResourceRecord * obj;
10253
10254 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
10255 require_action( obj, exit, err = kNoMemoryErr );
10256
10257 obj->name = inName;
10258 obj->type = inType;
10259 obj->class = inClass;
10260 obj->ttl = inTTL;
10261 obj->rdlength = inRDLength;
10262 obj->rdata = inRData;
10263
10264 if( inType == kDNSServiceType_SRV )
10265 {
10266 require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
10267 obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
10268 }
10269
10270 *outRecord = obj;
10271 obj = NULL;
10272 err = kNoErr;
10273
10274 exit:
10275 FreeNullSafe( obj );
10276 return( err );
10277 }
10278
10279 //===========================================================================================================================
10280 // _MRResourceRecordFree
10281 //===========================================================================================================================
10282
10283 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
10284 {
10285 ForgetMem( &inRecord->name );
10286 ForgetMem( &inRecord->rdata );
10287 free( inRecord );
10288 }
10289
10290 //===========================================================================================================================
10291 // _MRResourceRecordFreeList
10292 //===========================================================================================================================
10293
10294 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
10295 {
10296 MRResourceRecord * record;
10297
10298 while( ( record = inList ) != NULL )
10299 {
10300 inList = record->next;
10301 _MRResourceRecordFree( record );
10302 }
10303 }
10304
10305 //===========================================================================================================================
10306 // _MRNameOffsetItemCreate
10307 //===========================================================================================================================
10308
10309 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
10310 {
10311 OSStatus err;
10312 MRNameOffsetItem * obj;
10313 size_t nameLen;
10314
10315 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
10316
10317 nameLen = DomainNameLength( inName );
10318 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
10319 require_action( obj, exit, err = kNoMemoryErr );
10320
10321 obj->offset = inOffset;
10322 memcpy( obj->name, inName, nameLen );
10323
10324 *outItem = obj;
10325 err = kNoErr;
10326
10327 exit:
10328 return( err );
10329 }
10330
10331 //===========================================================================================================================
10332 // _MRNameOffsetItemFree
10333 //===========================================================================================================================
10334
10335 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
10336 {
10337 free( inItem );
10338 }
10339
10340 //===========================================================================================================================
10341 // _MRNameOffsetItemFreeList
10342 //===========================================================================================================================
10343
10344 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
10345 {
10346 MRNameOffsetItem * item;
10347
10348 while( ( item = inList ) != NULL )
10349 {
10350 inList = item->next;
10351 _MRNameOffsetItemFree( item );
10352 }
10353 }
10354
10355 //===========================================================================================================================
10356 // GAIPerfCmd
10357 //===========================================================================================================================
10358
10359 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10360
10361 typedef struct GAITesterPrivate * GAITesterRef;
10362 typedef struct GAITestCase GAITestCase;
10363
10364 typedef struct
10365 {
10366 const char * name; // Domain name that was resolved.
10367 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
10368 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
10369 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
10370 OSStatus error;
10371
10372 } GAITestItemResult;
10373
10374 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
10375 typedef void
10376 ( *GAITesterResultsHandler_f )(
10377 const char * inCaseTitle,
10378 NanoTime64 inCaseStartTime,
10379 NanoTime64 inCaseEndTime,
10380 const GAITestItemResult * inResultArray,
10381 size_t inResultCount,
10382 void * inContext );
10383
10384 typedef unsigned int GAITestAddrType;
10385 #define kGAITestAddrType_None 0
10386 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10387 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10388 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10389
10390 #define GAITestAddrTypeIsValid( X ) \
10391 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10392
10393 typedef struct
10394 {
10395 GAITesterRef tester; // GAI tester object.
10396 CFMutableArrayRef testCaseResults; // Array of test case results.
10397 unsigned int iterTimeLimitMs; // Amount of time to allow each iteration to complete.
10398 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10399 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
10400 unsigned int defaultIterCount; // Default test case iteration count.
10401 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
10402 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
10403 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
10404 OutputFormatType outputFormat; // Format of test results output.
10405 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10406 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
10407 Boolean testFailed; // True if at least one test case iteration failed.
10408
10409 } GAIPerfContext;
10410
10411 static void GAIPerfContextFree( GAIPerfContext *inContext );
10412 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
10413 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
10414 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
10415 static void
10416 GAIPerfResultsHandler(
10417 const char * inCaseTitle,
10418 NanoTime64 inCaseStartTime,
10419 NanoTime64 inCaseEndTime,
10420 const GAITestItemResult * inResultArray,
10421 size_t inResultCount,
10422 void * inContext );
10423 static void GAIPerfSignalHandler( void *inContext );
10424
10425 CFTypeID GAITesterGetTypeID( void );
10426 static OSStatus
10427 GAITesterCreate(
10428 dispatch_queue_t inQueue,
10429 unsigned int inCallDelayMs,
10430 int inServerDelayMs,
10431 int inServerDefaultTTL,
10432 Boolean inSkipPathEvaluation,
10433 Boolean inBadUDPMode,
10434 GAITesterRef * outTester );
10435 static void GAITesterStart( GAITesterRef inTester );
10436 static void GAITesterStop( GAITesterRef inTester );
10437 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
10438 static void
10439 GAITesterSetStopHandler(
10440 GAITesterRef inTester,
10441 GAITesterStopHandler_f inEventHandler,
10442 void * inEventContext );
10443 static void
10444 GAITesterSetResultsHandler(
10445 GAITesterRef inTester,
10446 GAITesterResultsHandler_f inResultsHandler,
10447 void * inResultsContext );
10448
10449 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
10450 static void GAITestCaseFree( GAITestCase *inCase );
10451 static OSStatus
10452 GAITestCaseAddItem(
10453 GAITestCase * inCase,
10454 unsigned int inAliasCount,
10455 unsigned int inAddressCount,
10456 int inTTL,
10457 GAITestAddrType inHasAddrs,
10458 GAITestAddrType inWantAddrs,
10459 unsigned int inTimeLimitMs,
10460 unsigned int inItemCount );
10461 static OSStatus
10462 GAITestCaseAddLocalHostItem(
10463 GAITestCase * inCase,
10464 GAITestAddrType inWantAddrs,
10465 unsigned int inTimeLimitMs,
10466 unsigned int inItemCount );
10467
10468 static void GAIPerfCmd( void )
10469 {
10470 OSStatus err;
10471 GAIPerfContext * context = NULL;
10472
10473 err = CheckRootUser();
10474 require_noerr_quiet( err, exit );
10475
10476 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
10477 require_noerr_quiet( err, exit );
10478
10479 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
10480 require_noerr_quiet( err, exit );
10481
10482 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
10483 require_noerr_quiet( err, exit );
10484
10485 err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
10486 require_noerr_quiet( err, exit );
10487
10488 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
10489 require_action( context, exit, err = kNoMemoryErr );
10490
10491 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
10492 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
10493
10494 context->iterTimeLimitMs = (unsigned int) gGAIPerf_IterationTimeLimitMs;
10495 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
10496 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
10497 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
10498 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
10499 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
10500
10501 if( gGAIPerf_OutputFilePath )
10502 {
10503 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
10504 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
10505 }
10506
10507 err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
10508 require_noerr_quiet( err, exit );
10509
10510 err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
10511 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
10512 require_noerr( err, exit );
10513
10514 check( gGAIPerf_TestSuite );
10515 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
10516 {
10517 err = GAIPerfAddBasicTestCases( context );
10518 require_noerr( err, exit );
10519 }
10520 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
10521 {
10522 err = GAIPerfAddAdvancedTestCases( context );
10523 require_noerr( err, exit );
10524 }
10525 else
10526 {
10527 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
10528 goto exit;
10529 }
10530
10531 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
10532 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
10533
10534 signal( SIGINT, SIG_IGN );
10535 err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource );
10536 require_noerr( err, exit );
10537 dispatch_resume( context->sigIntSource );
10538
10539 signal( SIGTERM, SIG_IGN );
10540 err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource );
10541 require_noerr( err, exit );
10542 dispatch_resume( context->sigTermSource );
10543
10544 GAITesterStart( context->tester );
10545 dispatch_main();
10546
10547 exit:
10548 if( context ) GAIPerfContextFree( context );
10549 exit( 1 );
10550 }
10551
10552 //===========================================================================================================================
10553 // GAIPerfContextFree
10554 //===========================================================================================================================
10555
10556 static void GAIPerfContextFree( GAIPerfContext *inContext )
10557 {
10558 ForgetCF( &inContext->tester );
10559 ForgetCF( &inContext->testCaseResults );
10560 ForgetMem( &inContext->outputFilePath );
10561 dispatch_source_forget( &inContext->sigIntSource );
10562 dispatch_source_forget( &inContext->sigTermSource );
10563 free( inContext );
10564 }
10565
10566 //===========================================================================================================================
10567 // GAIPerfAddAdvancedTestCases
10568 //===========================================================================================================================
10569
10570 #define kTestCaseTitleBufferSize 128
10571
10572 static void
10573 _GAIPerfWriteTestCaseTitle(
10574 char inBuffer[ kTestCaseTitleBufferSize ],
10575 unsigned int inCNAMERecordCount,
10576 unsigned int inARecordCount,
10577 unsigned int inAAAARecordCount,
10578 GAITestAddrType inRequested,
10579 unsigned int inIterationCount,
10580 Boolean inIterationsAreUnique );
10581 static void
10582 _GAIPerfWriteLocalHostTestCaseTitle(
10583 char inBuffer[ kTestCaseTitleBufferSize ],
10584 GAITestAddrType inRequested,
10585 unsigned int inIterationCount );
10586
10587 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10588 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10589
10590 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
10591 {
10592 OSStatus err;
10593 unsigned int aliasCount, addressCount, i;
10594 GAITestCase * testCase = NULL;
10595 char title[ kTestCaseTitleBufferSize ];
10596
10597 aliasCount = 0;
10598 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
10599 {
10600 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
10601 {
10602 // Add a test case to resolve a domain name with
10603 //
10604 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10605 //
10606 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10607 // requires server queries.
10608
10609 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10610 inContext->defaultIterCount, true );
10611
10612 err = GAITestCaseCreate( title, &testCase );
10613 require_noerr( err, exit );
10614
10615 for( i = 0; i < inContext->defaultIterCount; ++i )
10616 {
10617 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10618 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10619 require_noerr( err, exit );
10620 }
10621
10622 err = GAITesterAddTestCase( inContext->tester, testCase );
10623 require_noerr( err, exit );
10624 testCase = NULL;
10625
10626 // Add a test case to resolve a domain name with
10627 //
10628 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10629 //
10630 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10631 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10632 // ideally require no server queries, i.e., the results should come from the cache.
10633
10634 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10635 inContext->defaultIterCount, false );
10636
10637 err = GAITestCaseCreate( title, &testCase );
10638 require_noerr( err, exit );
10639
10640 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10641 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
10642 require_noerr( err, exit );
10643
10644 err = GAITesterAddTestCase( inContext->tester, testCase );
10645 require_noerr( err, exit );
10646 testCase = NULL;
10647 }
10648
10649 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
10650 }
10651
10652 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10653
10654 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10655
10656 err = GAITestCaseCreate( title, &testCase );
10657 require_noerr( err, exit );
10658
10659 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10660 inContext->defaultIterCount );
10661 require_noerr( err, exit );
10662
10663 err = GAITesterAddTestCase( inContext->tester, testCase );
10664 require_noerr( err, exit );
10665 testCase = NULL;
10666
10667 exit:
10668 if( testCase ) GAITestCaseFree( testCase );
10669 return( err );
10670 }
10671
10672 //===========================================================================================================================
10673 // _GAIPerfWriteTestCaseTitle
10674 //===========================================================================================================================
10675
10676 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10677 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10678 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10679 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10680 "" )
10681
10682 static void
10683 _GAIPerfWriteTestCaseTitle(
10684 char inBuffer[ kTestCaseTitleBufferSize ],
10685 unsigned int inCNAMERecordCount,
10686 unsigned int inARecordCount,
10687 unsigned int inAAAARecordCount,
10688 GAITestAddrType inRequested,
10689 unsigned int inIterationCount,
10690 Boolean inIterationsAreUnique )
10691 {
10692 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10693 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
10694 inIterationCount, inIterationsAreUnique, ",unique" );
10695 }
10696
10697 //===========================================================================================================================
10698 // _GAIPerfWriteLocalHostTestCaseTitle
10699 //===========================================================================================================================
10700
10701 static void
10702 _GAIPerfWriteLocalHostTestCaseTitle(
10703 char inBuffer[ kTestCaseTitleBufferSize ],
10704 GAITestAddrType inRequested,
10705 unsigned int inIterationCount )
10706 {
10707 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
10708 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
10709 }
10710
10711 //===========================================================================================================================
10712 // GAIPerfAddBasicTestCases
10713 //===========================================================================================================================
10714
10715 #define kGAIPerfBasicTestSuite_AliasCount 2
10716 #define kGAIPerfBasicTestSuite_AddrCount 4
10717
10718 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
10719 {
10720 OSStatus err;
10721 GAITestCase * testCase = NULL;
10722 char title[ kTestCaseTitleBufferSize ];
10723 unsigned int i;
10724
10725 // Test Case #1:
10726 // Resolve a domain name with
10727 //
10728 // 2 CNAME records, 4 A records, and 4 AAAA records
10729 //
10730 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10731 // queries.
10732
10733 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10734 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10735 inContext->defaultIterCount, true );
10736
10737 err = GAITestCaseCreate( title, &testCase );
10738 require_noerr( err, exit );
10739
10740 for( i = 0; i < inContext->defaultIterCount; ++i )
10741 {
10742 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10743 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10744 require_noerr( err, exit );
10745 }
10746
10747 err = GAITesterAddTestCase( inContext->tester, testCase );
10748 require_noerr( err, exit );
10749 testCase = NULL;
10750
10751 // Test Case #2:
10752 // Resolve a domain name with
10753 //
10754 // 2 CNAME records, 4 A records, and 4 AAAA records
10755 //
10756 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10757 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10758 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10759
10760 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10761 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10762 inContext->defaultIterCount, false );
10763
10764 err = GAITestCaseCreate( title, &testCase );
10765 require_noerr( err, exit );
10766
10767 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10768 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10769 inContext->defaultIterCount + 1 );
10770 require_noerr( err, exit );
10771
10772 err = GAITesterAddTestCase( inContext->tester, testCase );
10773 require_noerr( err, exit );
10774 testCase = NULL;
10775
10776 // Test Case #3:
10777 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10778
10779 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10780
10781 err = GAITestCaseCreate( title, &testCase );
10782 require_noerr( err, exit );
10783
10784 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10785 inContext->defaultIterCount );
10786 require_noerr( err, exit );
10787
10788 err = GAITesterAddTestCase( inContext->tester, testCase );
10789 require_noerr( err, exit );
10790 testCase = NULL;
10791
10792 exit:
10793 if( testCase ) GAITestCaseFree( testCase );
10794 return( err );
10795 }
10796
10797 //===========================================================================================================================
10798 // GAIPerfTesterStopHandler
10799 //===========================================================================================================================
10800
10801 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10802 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10803 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10804
10805 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10806 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10807 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10808 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10809
10810 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
10811 {
10812 OSStatus err;
10813 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10814 CFPropertyListRef plist;
10815 int exitCode;
10816
10817 err = inError;
10818 require_noerr_quiet( err, exit );
10819
10820 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
10821 "{"
10822 "%kO=" // info
10823 "{"
10824 "%kO=%lli" // callDelayMs
10825 "%kO=%lli" // serverDelayMs
10826 "%kO=%b" // skippedPathEval
10827 "%kO=%b" // usedBadUPDMode
10828 "}"
10829 "%kO=%O" // testCases
10830 "%kO=%b" // success
10831 "}",
10832 kGAIPerfResultsKey_Info,
10833 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
10834 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
10835 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
10836 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
10837 kGAIPerfResultsKey_TestCases, context->testCaseResults,
10838 kGAIPerfResultsKey_Success, !context->testFailed );
10839 require_noerr( err, exit );
10840
10841 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
10842 CFRelease( plist );
10843 require_noerr( err, exit );
10844
10845 exit:
10846 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
10847 GAIPerfContextFree( context );
10848 exit( exitCode );
10849 }
10850
10851 //===========================================================================================================================
10852 // GAIPerfResultsHandler
10853 //===========================================================================================================================
10854
10855 // Keys for test case dictionary
10856
10857 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10858 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10859 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10860 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10861 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10862 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10863 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10864
10865 // Keys for test case results array entry dictionaries
10866
10867 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10868 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10869 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10870 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10871
10872 // Keys for test case stats dictionaries
10873
10874 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10875 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10876 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10877 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10878 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10879
10880 typedef struct
10881 {
10882 double min;
10883 double max;
10884 double mean;
10885 double stdDev;
10886
10887 } GAIPerfStats;
10888
10889 #define GAIPerfStatsInit( X ) \
10890 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10891
10892 static void
10893 GAIPerfResultsHandler(
10894 const char * inCaseTitle,
10895 NanoTime64 inCaseStartTime,
10896 NanoTime64 inCaseEndTime,
10897 const GAITestItemResult * inResultArray,
10898 size_t inResultCount,
10899 void * inContext )
10900 {
10901 OSStatus err;
10902 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10903 int namesAreDynamic, namesAreUnique;
10904 const char * ptr;
10905 size_t count, startIndex;
10906 CFMutableArrayRef results = NULL;
10907 GAIPerfStats stats, firstStats, connStats;
10908 double sum, firstSum, connSum;
10909 size_t keyValueLen, i;
10910 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10911 char startTime[ 32 ];
10912 char endTime[ 32 ];
10913 const GAITestItemResult * result;
10914
10915 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10916 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10917 // the domain name's CNAME, A, and AAAA records.
10918
10919 namesAreDynamic = false;
10920 namesAreUnique = false;
10921 ptr = inCaseTitle;
10922 while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
10923 {
10924 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
10925 {
10926 namesAreDynamic = true;
10927 }
10928 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
10929 {
10930 namesAreUnique = true;
10931 }
10932 if( namesAreDynamic && namesAreUnique ) break;
10933 }
10934
10935 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
10936 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
10937 require_action( results, exit, err = kNoMemoryErr );
10938
10939 GAIPerfStatsInit( &stats );
10940 GAIPerfStatsInit( &firstStats );
10941 GAIPerfStatsInit( &connStats );
10942
10943 sum = 0.0;
10944 firstSum = 0.0;
10945 connSum = 0.0;
10946 count = 0;
10947 for( i = startIndex; i < inResultCount; ++i )
10948 {
10949 double value;
10950
10951 result = &inResultArray[ i ];
10952
10953 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
10954 "{"
10955 "%kO=%s" // name
10956 "%kO=%lli" // connectionTimeUs
10957 "%kO=%lli" // firstTimeUs
10958 "%kO=%lli" // timeUs
10959 "%kO=%lli" // error
10960 "}",
10961 kGAIPerfTestCaseResultKey_Name, result->name,
10962 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
10963 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
10964 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
10965 CFSTR( "error" ), (int64_t) result->error );
10966 require_noerr( err, exit );
10967
10968 if( !result->error )
10969 {
10970 value = (double) result->timeUs;
10971 if( value < stats.min ) stats.min = value;
10972 if( value > stats.max ) stats.max = value;
10973 sum += value;
10974
10975 value = (double) result->firstTimeUs;
10976 if( value < firstStats.min ) firstStats.min = value;
10977 if( value > firstStats.max ) firstStats.max = value;
10978 firstSum += value;
10979
10980 value = (double) result->connectionTimeUs;
10981 if( value < connStats.min ) connStats.min = value;
10982 if( value > connStats.max ) connStats.max = value;
10983 connSum += value;
10984
10985 ++count;
10986 }
10987 else
10988 {
10989 context->testFailed = true;
10990 }
10991 }
10992
10993 if( count > 0 )
10994 {
10995 stats.mean = sum / count;
10996 firstStats.mean = firstSum / count;
10997 connStats.mean = connSum / count;
10998
10999 sum = 0.0;
11000 firstSum = 0.0;
11001 connSum = 0.0;
11002 for( i = startIndex; i < inResultCount; ++i )
11003 {
11004 double diff;
11005
11006 result = &inResultArray[ i ];
11007 if( result->error ) continue;
11008
11009 diff = stats.mean - (double) result->timeUs;
11010 sum += ( diff * diff );
11011
11012 diff = firstStats.mean - (double) result->firstTimeUs;
11013 firstSum += ( diff * diff );
11014
11015 diff = connStats.mean - (double) result->connectionTimeUs;
11016 connSum += ( diff * diff );
11017 }
11018 stats.stdDev = sqrt( sum / count );
11019 firstStats.stdDev = sqrt( firstSum / count );
11020 connStats.stdDev = sqrt( connSum / count );
11021 }
11022
11023 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
11024 "{"
11025 "%kO=%s"
11026 "%kO=%s"
11027 "%kO=%s"
11028 "%kO=%O"
11029 "%kO="
11030 "{"
11031 "%kO=%lli"
11032 "%kO=%f"
11033 "%kO=%f"
11034 "%kO=%f"
11035 "%kO=%f"
11036 "}"
11037 "%kO="
11038 "{"
11039 "%kO=%lli"
11040 "%kO=%f"
11041 "%kO=%f"
11042 "%kO=%f"
11043 "%kO=%f"
11044 "}"
11045 "%kO="
11046 "{"
11047 "%kO=%lli"
11048 "%kO=%f"
11049 "%kO=%f"
11050 "%kO=%f"
11051 "%kO=%f"
11052 "}"
11053 "}",
11054 kGAIPerfTestCaseKey_Title, inCaseTitle,
11055 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
11056 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
11057 kGAIPerfTestCaseKey_Results, results,
11058 kGAIPerfTestCaseKey_Stats,
11059 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11060 kGAIPerfTestCaseStatsKey_Min, stats.min,
11061 kGAIPerfTestCaseStatsKey_Max, stats.max,
11062 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
11063 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
11064 kGAIPerfTestCaseKey_FirstStats,
11065 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11066 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
11067 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
11068 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
11069 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
11070 kGAIPerfTestCaseKey_ConnectionStats,
11071 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11072 kGAIPerfTestCaseStatsKey_Min, connStats.min,
11073 kGAIPerfTestCaseStatsKey_Max, connStats.max,
11074 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
11075 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
11076 require_noerr( err, exit );
11077
11078 exit:
11079 CFReleaseNullSafe( results );
11080 if( err ) exit( 1 );
11081 }
11082
11083 //===========================================================================================================================
11084 // GAIPerfSignalHandler
11085 //===========================================================================================================================
11086
11087 static void GAIPerfSignalHandler( void *inContext )
11088 {
11089 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11090
11091 if( !context->tester ) exit( 1 );
11092 GAITesterStop( context->tester );
11093 context->tester = NULL;
11094 }
11095
11096 //===========================================================================================================================
11097 // GAITesterCreate
11098 //===========================================================================================================================
11099
11100 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11101 // possible strings to use in the Tag label.
11102
11103 #define kGAITesterTagStringLen 6
11104
11105 typedef struct GAITestItem GAITestItem;
11106 struct GAITestItem
11107 {
11108 GAITestItem * next; // Next test item in list.
11109 char * name; // Domain name to resolve.
11110 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
11111 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
11112 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
11113 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
11114 OSStatus error; // Current status/error.
11115 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
11116 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
11117 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
11118 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11119 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11120 };
11121
11122 struct GAITestCase
11123 {
11124 GAITestCase * next; // Next test case in list.
11125 GAITestItem * itemList; // List of test items.
11126 char * title; // Title of the test case.
11127 };
11128
11129 struct GAITesterPrivate
11130 {
11131 CFRuntimeBase base; // CF object base.
11132 dispatch_queue_t queue; // Serial work queue.
11133 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
11134 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
11135 GAITestCase * caseList; // List of test cases.
11136 GAITestCase * currentCase; // Pointer to the current test case.
11137 GAITestItem * currentItem; // Pointer to the current test item.
11138 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
11139 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
11140 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11141 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11142 Boolean stopped; // True if the tester has been stopped.
11143 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
11144 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
11145 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
11146 pid_t serverPID; // PID of the test DNS server.
11147 int serverDelayMs; // Additional time to have the server delay its responses by.
11148 int serverDefaultTTL; // Default TTL for the server's records.
11149 GAITesterStopHandler_f stopHandler; // User's stop handler.
11150 void * stopContext; // User's event handler context.
11151 GAITesterResultsHandler_f resultsHandler; // User's results handler.
11152 void * resultsContext; // User's results handler context.
11153
11154 // Variables for current test item.
11155
11156 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
11157 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
11158 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
11159 uint64_t connTicks; // Ticks when the connection was created.
11160 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
11161 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
11162 Boolean gotFirstResult; // True if the first result has been received.
11163 };
11164
11165 CF_CLASS_DEFINE( GAITester );
11166
11167 static void _GAITesterStartNextTest( GAITesterRef inTester );
11168 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
11169 static void _GAITesterFirstGAITimeout( void *inContext );
11170 static void _GAITesterTimeout( void *inContext );
11171 static void DNSSD_API
11172 _GAITesterFirstGAICallback(
11173 DNSServiceRef inSDRef,
11174 DNSServiceFlags inFlags,
11175 uint32_t inInterfaceIndex,
11176 DNSServiceErrorType inError,
11177 const char * inHostname,
11178 const struct sockaddr * inSockAddr,
11179 uint32_t inTTL,
11180 void * inContext );
11181 static void DNSSD_API
11182 _GAITesterGetAddrInfoCallback(
11183 DNSServiceRef inSDRef,
11184 DNSServiceFlags inFlags,
11185 uint32_t inInterfaceIndex,
11186 DNSServiceErrorType inError,
11187 const char * inHostname,
11188 const struct sockaddr * inSockAddr,
11189 uint32_t inTTL,
11190 void * inContext );
11191 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
11192
11193 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11194
11195 static OSStatus
11196 GAITestItemCreate(
11197 const char * inName,
11198 unsigned int inAddressCount,
11199 GAITestAddrType inHasAddrs,
11200 GAITestAddrType inWantAddrs,
11201 unsigned int inTimeLimitMs,
11202 GAITestItem ** outItem );
11203 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
11204 static void GAITestItemFree( GAITestItem *inItem );
11205
11206 static OSStatus
11207 GAITesterCreate(
11208 dispatch_queue_t inQueue,
11209 unsigned int inCallDelayMs,
11210 int inServerDelayMs,
11211 int inServerDefaultTTL,
11212 Boolean inSkipPathEvaluation,
11213 Boolean inBadUDPMode,
11214 GAITesterRef * outTester )
11215 {
11216 OSStatus err;
11217 GAITesterRef obj = NULL;
11218
11219 CF_OBJECT_CREATE( GAITester, obj, err, exit );
11220
11221 ReplaceDispatchQueue( &obj->queue, inQueue );
11222 obj->callDelayMs = inCallDelayMs;
11223 obj->serverPID = -1;
11224 obj->serverDelayMs = inServerDelayMs;
11225 obj->serverDefaultTTL = inServerDefaultTTL;
11226 obj->skipPathEval = inSkipPathEvaluation;
11227 obj->badUDPMode = inBadUDPMode;
11228
11229 *outTester = obj;
11230 obj = NULL;
11231 err = kNoErr;
11232
11233 exit:
11234 CFReleaseNullSafe( obj );
11235 return( err );
11236 }
11237
11238 //===========================================================================================================================
11239 // _GAITesterFinalize
11240 //===========================================================================================================================
11241
11242 static void _GAITesterFinalize( CFTypeRef inObj )
11243 {
11244 GAITesterRef const me = (GAITesterRef) inObj;
11245 GAITestCase * testCase;
11246
11247 check( !me->getAddrInfo );
11248 check( !me->connection );
11249 check( !me->timer );
11250 dispatch_forget( &me->queue );
11251 while( ( testCase = me->caseList ) != NULL )
11252 {
11253 me->caseList = testCase->next;
11254 GAITestCaseFree( testCase );
11255 }
11256 }
11257
11258 //===========================================================================================================================
11259 // GAITesterStart
11260 //===========================================================================================================================
11261
11262 static void _GAITesterStart( void *inContext );
11263 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
11264
11265 static void GAITesterStart( GAITesterRef me )
11266 {
11267 CFRetain( me );
11268 dispatch_async_f( me->queue, me, _GAITesterStart );
11269 }
11270
11271 #define kGAITesterFirstGAITimeoutSecs 4
11272
11273 static void _GAITesterStart( void *inContext )
11274 {
11275 OSStatus err;
11276 GAITesterRef const me = (GAITesterRef) inContext;
11277 DNSServiceFlags flags;
11278 char name[ 64 ];
11279 char tag[ kGAITesterTagStringLen + 1 ];
11280
11281 err = SpawnCommand( &me->serverPID, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11282 (int64_t) getpid(),
11283 me->serverDefaultTTL >= 0, " --defaultTTL ",
11284 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
11285 me->serverDelayMs >= 0, " --responseDelay ",
11286 me->serverDelayMs >= 0, me->serverDelayMs,
11287 me->badUDPMode, " --badUDPMode" );
11288 require_noerr_quiet( err, exit );
11289
11290 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test",
11291 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
11292
11293 flags = 0;
11294 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11295
11296 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
11297 _GAITesterFirstGAICallback, me );
11298 require_noerr( err, exit );
11299
11300 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
11301 require_noerr( err, exit );
11302
11303 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
11304 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
11305 _GAITesterFirstGAITimeout, me, &me->timer );
11306 require_noerr( err, exit );
11307 dispatch_resume( me->timer );
11308
11309 exit:
11310 if( err ) _GAITesterStop( me, err );
11311 }
11312
11313 //===========================================================================================================================
11314 // GAITesterStop
11315 //===========================================================================================================================
11316
11317 static void _GAITesterUserStop( void *inContext );
11318
11319 static void GAITesterStop( GAITesterRef me )
11320 {
11321 CFRetain( me );
11322 dispatch_async_f( me->queue, me, _GAITesterUserStop );
11323 }
11324
11325 static void _GAITesterUserStop( void *inContext )
11326 {
11327 GAITesterRef const me = (GAITesterRef) inContext;
11328
11329 _GAITesterStop( me, kCanceledErr );
11330 CFRelease( me );
11331 }
11332
11333 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
11334 {
11335 OSStatus err;
11336
11337 ForgetPacketCapture( &me->pcap );
11338 dispatch_source_forget( &me->timer );
11339 DNSServiceForget( &me->getAddrInfo );
11340 DNSServiceForget( &me->connection );
11341 if( me->serverPID != -1 )
11342 {
11343 err = kill( me->serverPID, SIGTERM );
11344 err = map_global_noerr_errno( err );
11345 check_noerr( err );
11346 me->serverPID = -1;
11347 }
11348
11349 if( !me->stopped )
11350 {
11351 me->stopped = true;
11352 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
11353 CFRelease( me );
11354 }
11355 }
11356
11357 //===========================================================================================================================
11358 // GAITesterAddTestCase
11359 //===========================================================================================================================
11360
11361 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
11362 {
11363 OSStatus err;
11364 GAITestCase ** ptr;
11365
11366 require_action_quiet( inCase->itemList, exit, err = kCountErr );
11367
11368 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
11369 *ptr = inCase;
11370 err = kNoErr;
11371
11372 exit:
11373 return( err );
11374 }
11375
11376 //===========================================================================================================================
11377 // GAITesterSetStopHandler
11378 //===========================================================================================================================
11379
11380 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
11381 {
11382 me->stopHandler = inStopHandler;
11383 me->stopContext = inStopContext;
11384 }
11385
11386 //===========================================================================================================================
11387 // GAITesterSetResultsHandler
11388 //===========================================================================================================================
11389
11390 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
11391 {
11392 me->resultsHandler = inResultsHandler;
11393 me->resultsContext = inResultsContext;
11394 }
11395
11396 //===========================================================================================================================
11397 // _GAITesterStartNextTest
11398 //===========================================================================================================================
11399
11400 static void _GAITesterStartNextTest( GAITesterRef me )
11401 {
11402 OSStatus err;
11403 GAITestItem * item;
11404 DNSServiceFlags flags;
11405 DNSServiceProtocol protocols;
11406 int done = false;
11407
11408 if( me->currentItem ) me->currentItem = me->currentItem->next;
11409
11410 if( !me->currentItem )
11411 {
11412 if( me->currentCase )
11413 {
11414 // No more test items means that the current test case has completed.
11415
11416 me->caseEndTime = NanoTimeGetCurrent();
11417
11418 if( me->resultsHandler )
11419 {
11420 size_t resultCount, i;
11421 GAITestItemResult * resultArray;
11422
11423 resultCount = 0;
11424 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
11425 check( resultCount > 0 );
11426
11427 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
11428 require_action( resultArray, exit, err = kNoMemoryErr );
11429
11430 item = me->currentCase->itemList;
11431 for( i = 0; i < resultCount; ++i )
11432 {
11433 resultArray[ i ].name = item->name;
11434 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
11435 resultArray[ i ].firstTimeUs = item->firstTimeUs;
11436 resultArray[ i ].timeUs = item->timeUs;
11437 resultArray[ i ].error = item->error;
11438 item = item->next;
11439 }
11440 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
11441 me->resultsContext );
11442 ForgetMem( &resultArray );
11443 }
11444
11445 me->currentCase = me->currentCase->next;
11446 if( !me->currentCase )
11447 {
11448 done = true;
11449 err = kNoErr;
11450 goto exit;
11451 }
11452 }
11453 else
11454 {
11455 me->currentCase = me->caseList;
11456 }
11457 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
11458 me->currentItem = me->currentCase->itemList;
11459 }
11460
11461 item = me->currentItem;
11462 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
11463
11464 if( !item->wantV4 ) me->bitmapV4 = 0;
11465 else if( !item->hasV4 ) me->bitmapV4 = 1;
11466 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11467 else me->bitmapV4 = ~UINT64_C( 0 );
11468
11469 if( !item->wantV6 ) me->bitmapV6 = 0;
11470 else if( !item->hasV6 ) me->bitmapV6 = 1;
11471 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11472 else me->bitmapV6 = ~UINT64_C( 0 );
11473 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
11474 me->gotFirstResult = false;
11475
11476 // Perform preliminary tasks if this is the start of a new test case.
11477
11478 if( item == me->currentCase->itemList )
11479 {
11480 // Flush mDNSResponder's cache.
11481
11482 err = systemf( NULL, "killall -HUP mDNSResponder" );
11483 require_noerr( err, exit );
11484 sleep( 1 );
11485
11486 me->caseStartTime = NanoTimeGetCurrent();
11487 me->caseEndTime = kNanoTime_Invalid;
11488 }
11489
11490 // Start a packet capture.
11491
11492 check( !me->pcap );
11493 err = _GAITesterCreatePacketCapture( &me->pcap );
11494 require_noerr( err, exit );
11495
11496 // Start timer for test item's time limit.
11497
11498 check( !me->timer );
11499 if( item->timeLimitMs > 0 )
11500 {
11501 unsigned int timeLimitMs;
11502
11503 timeLimitMs = item->timeLimitMs;
11504 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
11505 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
11506
11507 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
11508 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
11509 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
11510 require_noerr( err, exit );
11511 dispatch_resume( me->timer );
11512 }
11513
11514 // Call DNSServiceGetAddrInfo().
11515
11516 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
11517
11518 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
11519 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11520
11521 protocols = 0;
11522 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
11523 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
11524
11525 me->startTicks = UpTicks();
11526
11527 check( !me->connection );
11528 err = DNSServiceCreateConnection( &me->connection );
11529 require_noerr( err, exit );
11530
11531 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
11532 require_noerr( err, exit );
11533
11534 me->connTicks = UpTicks();
11535
11536 check( !me->getAddrInfo );
11537 me->getAddrInfo = me->connection;
11538 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
11539 _GAITesterGetAddrInfoCallback, me );
11540 require_noerr( err, exit );
11541
11542 exit:
11543 if( err || done ) _GAITesterStop( me, err );
11544 }
11545
11546 //===========================================================================================================================
11547 // _GAITesterCreatePacketCapture
11548 //===========================================================================================================================
11549
11550 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
11551 {
11552 OSStatus err;
11553 pcap_t * pcap;
11554 struct bpf_program program;
11555 char errBuf[ PCAP_ERRBUF_SIZE ];
11556
11557 pcap = pcap_create( "lo0", errBuf );
11558 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
11559
11560 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
11561 require_noerr_action( err, exit, err = kUnknownErr );
11562
11563 err = pcap_set_snaplen( pcap, 512 );
11564 require_noerr_action( err, exit, err = kUnknownErr );
11565
11566 err = pcap_set_immediate_mode( pcap, 0 );
11567 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11568
11569 err = pcap_activate( pcap );
11570 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11571
11572 err = pcap_setdirection( pcap, PCAP_D_INOUT );
11573 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11574
11575 err = pcap_setnonblock( pcap, 1, errBuf );
11576 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
11577
11578 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
11579 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11580
11581 err = pcap_setfilter( pcap, &program );
11582 pcap_freecode( &program );
11583 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11584
11585 *outPCap = pcap;
11586 pcap = NULL;
11587
11588 exit:
11589 if( pcap ) pcap_close( pcap );
11590 return( err );
11591 }
11592
11593 //===========================================================================================================================
11594 // _GAITesterFirstGAITimeout
11595 //===========================================================================================================================
11596
11597 static void _GAITesterFirstGAITimeout( void *inContext )
11598 {
11599 GAITesterRef const me = (GAITesterRef) inContext;
11600
11601 _GAITesterStop( me, kNoResourcesErr );
11602 }
11603
11604 //===========================================================================================================================
11605 // _GAITesterTimeout
11606 //===========================================================================================================================
11607
11608 static void _GAITesterTimeout( void *inContext )
11609 {
11610 GAITesterRef const me = (GAITesterRef) inContext;
11611
11612 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
11613 }
11614
11615 //===========================================================================================================================
11616 // _GAITesterFirstGAICallback
11617 //===========================================================================================================================
11618
11619 static void DNSSD_API
11620 _GAITesterFirstGAICallback(
11621 DNSServiceRef inSDRef,
11622 DNSServiceFlags inFlags,
11623 uint32_t inInterfaceIndex,
11624 DNSServiceErrorType inError,
11625 const char * inHostname,
11626 const struct sockaddr * inSockAddr,
11627 uint32_t inTTL,
11628 void * inContext )
11629 {
11630 GAITesterRef const me = (GAITesterRef) inContext;
11631
11632 Unused( inSDRef );
11633 Unused( inInterfaceIndex );
11634 Unused( inHostname );
11635 Unused( inSockAddr );
11636 Unused( inTTL );
11637
11638 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
11639 {
11640 dispatch_source_forget( &me->timer );
11641 DNSServiceForget( &me->getAddrInfo );
11642
11643 _GAITesterStartNextTest( me );
11644 }
11645 }
11646
11647 //===========================================================================================================================
11648 // _GAITesterGetAddrInfoCallback
11649 //===========================================================================================================================
11650
11651 static void DNSSD_API
11652 _GAITesterGetAddrInfoCallback(
11653 DNSServiceRef inSDRef,
11654 DNSServiceFlags inFlags,
11655 uint32_t inInterfaceIndex,
11656 DNSServiceErrorType inError,
11657 const char * inHostname,
11658 const struct sockaddr * inSockAddr,
11659 uint32_t inTTL,
11660 void * inContext )
11661 {
11662 OSStatus err;
11663 GAITesterRef const me = (GAITesterRef) inContext;
11664 GAITestItem * const item = me->currentItem;
11665 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
11666 uint64_t nowTicks;
11667 uint64_t * bitmapPtr;
11668 uint64_t bitmask;
11669 int hasAddr;
11670
11671 Unused( inSDRef );
11672 Unused( inInterfaceIndex );
11673 Unused( inHostname );
11674 Unused( inTTL );
11675
11676 nowTicks = UpTicks();
11677
11678 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
11679
11680 // Check if we were expecting an IP address result of this type.
11681
11682 if( sip->sa.sa_family == AF_INET )
11683 {
11684 bitmapPtr = &me->bitmapV4;
11685 hasAddr = item->hasV4;
11686 }
11687 else if( sip->sa.sa_family == AF_INET6 )
11688 {
11689 bitmapPtr = &me->bitmapV6;
11690 hasAddr = item->hasV6;
11691 }
11692 else
11693 {
11694 err = kTypeErr;
11695 goto exit;
11696 }
11697
11698 bitmask = 0;
11699 if( hasAddr )
11700 {
11701 uint32_t addrOffset;
11702
11703 require_noerr_action_quiet( inError, exit, err = inError );
11704
11705 if( sip->sa.sa_family == AF_INET )
11706 {
11707 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
11708
11709 if( strcasecmp( item->name, "localhost." ) == 0 )
11710 {
11711 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
11712 }
11713 else
11714 {
11715 addrOffset = addrV4 - kDNSServerBaseAddrV4;
11716 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11717 {
11718 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11719 }
11720 }
11721 }
11722 else
11723 {
11724 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
11725
11726 if( strcasecmp( item->name, "localhost." ) == 0 )
11727 {
11728 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
11729 }
11730 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
11731 {
11732 addrOffset = addrV6[ 15 ];
11733 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11734 {
11735 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11736 }
11737 }
11738 }
11739 }
11740 else
11741 {
11742 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
11743 bitmask = 1;
11744 }
11745 require_action_quiet( bitmask != 0, exit, err = kValueErr );
11746 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
11747
11748 *bitmapPtr &= ~bitmask;
11749 if( !me->gotFirstResult )
11750 {
11751 me->firstTicks = nowTicks;
11752 me->gotFirstResult = true;
11753 }
11754 err = kNoErr;
11755
11756 exit:
11757 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
11758 {
11759 me->endTicks = nowTicks;
11760 _GAITesterCompleteCurrentTest( me, err );
11761 }
11762 }
11763
11764 //===========================================================================================================================
11765 // _GAITesterCompleteCurrentTest
11766 //===========================================================================================================================
11767
11768 static OSStatus
11769 _GAITesterGetDNSMessageFromPacket(
11770 const uint8_t * inPacketPtr,
11771 size_t inPacketLen,
11772 const uint8_t ** outMsgPtr,
11773 size_t * outMsgLen );
11774
11775 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
11776 {
11777 OSStatus err;
11778 GAITestItem * const item = me->currentItem;
11779 struct timeval timeStamps[ 4 ];
11780 struct timeval * tsPtr;
11781 struct timeval * tsQA = NULL;
11782 struct timeval * tsQAAAA = NULL;
11783 struct timeval * tsRA = NULL;
11784 struct timeval * tsRAAAA = NULL;
11785 struct timeval * t1;
11786 struct timeval * t2;
11787 int64_t idleTimeUs;
11788 uint8_t name[ kDomainNameLengthMax ];
11789
11790 dispatch_source_forget( &me->timer );
11791 DNSServiceForget( &me->getAddrInfo );
11792 DNSServiceForget( &me->connection );
11793
11794 item->error = inError;
11795 if( item->error )
11796 {
11797 err = kNoErr;
11798 goto exit;
11799 }
11800
11801 err = DomainNameFromString( name, item->name, NULL );
11802 require_noerr( err, exit );
11803
11804 tsPtr = &timeStamps[ 0 ];
11805 for( ;; )
11806 {
11807 int status;
11808 struct pcap_pkthdr * pktHdr;
11809 const uint8_t * packet;
11810 const uint8_t * msgPtr;
11811 size_t msgLen;
11812 const DNSHeader * hdr;
11813 unsigned int flags;
11814 const uint8_t * ptr;
11815 uint16_t qtype, qclass;
11816 uint8_t qname[ kDomainNameLengthMax ];
11817
11818 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
11819 if( status != 1 ) break;
11820 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
11821 if( msgLen < kDNSHeaderLength ) continue;
11822
11823 hdr = (const DNSHeader *) msgPtr;
11824 flags = DNSHeaderGetFlags( hdr );
11825 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
11826 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
11827
11828 ptr = (const uint8_t *) &hdr[ 1 ];
11829 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
11830 if( qclass != kDNSServiceClass_IN ) continue;
11831 if( !DomainNameEqual( qname, name ) ) continue;
11832
11833 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
11834 {
11835 if( flags & kDNSHeaderFlag_Response )
11836 {
11837 if( tsQA && !tsRA )
11838 {
11839 tsRA = tsPtr++;
11840 *tsRA = pktHdr->ts;
11841 }
11842 }
11843 else if( !tsQA )
11844 {
11845 tsQA = tsPtr++;
11846 *tsQA = pktHdr->ts;
11847 }
11848 }
11849 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
11850 {
11851 if( flags & kDNSHeaderFlag_Response )
11852 {
11853 if( tsQAAAA && !tsRAAAA )
11854 {
11855 tsRAAAA = tsPtr++;
11856 *tsRAAAA = pktHdr->ts;
11857 }
11858 }
11859 else if( !tsQAAAA )
11860 {
11861 tsQAAAA = tsPtr++;
11862 *tsQAAAA = pktHdr->ts;
11863 }
11864 }
11865 }
11866
11867 // t1 is the time when the last query was sent.
11868
11869 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
11870 else t1 = tsQA ? tsQA : tsQAAAA;
11871
11872 // t2 is when the first response was received.
11873
11874 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
11875 else t2 = tsRA ? tsRA : tsRAAAA;
11876
11877 if( t1 && t2 )
11878 {
11879 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
11880 if( idleTimeUs < 0 ) idleTimeUs = 0;
11881 }
11882 else
11883 {
11884 idleTimeUs = 0;
11885 }
11886
11887 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
11888 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11889 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11890
11891 exit:
11892 ForgetPacketCapture( &me->pcap );
11893 if( err ) _GAITesterStop( me, err );
11894 else _GAITesterStartNextTest( me );
11895 }
11896
11897 //===========================================================================================================================
11898 // _GAITesterGetDNSMessageFromPacket
11899 //===========================================================================================================================
11900
11901 #define kHeaderSizeNullLink 4
11902 #define kHeaderSizeIPv4Min 20
11903 #define kHeaderSizeIPv6 40
11904 #define kHeaderSizeUDP 8
11905
11906 #define kIPProtocolUDP 0x11
11907
11908 static OSStatus
11909 _GAITesterGetDNSMessageFromPacket(
11910 const uint8_t * inPacketPtr,
11911 size_t inPacketLen,
11912 const uint8_t ** outMsgPtr,
11913 size_t * outMsgLen )
11914 {
11915 OSStatus err;
11916 const uint8_t * nullLink;
11917 uint32_t addressFamily;
11918 const uint8_t * ip;
11919 int ipHeaderLen;
11920 int protocol;
11921 const uint8_t * msg;
11922 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
11923
11924 nullLink = &inPacketPtr[ 0 ];
11925 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
11926 addressFamily = ReadHost32( &nullLink[ 0 ] );
11927
11928 ip = &nullLink[ kHeaderSizeNullLink ];
11929 if( addressFamily == AF_INET )
11930 {
11931 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
11932 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
11933 protocol = ip[ 9 ];
11934 }
11935 else if( addressFamily == AF_INET6 )
11936 {
11937 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
11938 ipHeaderLen = kHeaderSizeIPv6;
11939 protocol = ip[ 6 ];
11940 }
11941 else
11942 {
11943 err = kTypeErr;
11944 goto exit;
11945 }
11946 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
11947 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
11948
11949 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
11950
11951 *outMsgPtr = msg;
11952 *outMsgLen = (size_t)( end - msg );
11953 err = kNoErr;
11954
11955 exit:
11956 return( err );
11957 }
11958
11959 //===========================================================================================================================
11960 // GAITestCaseCreate
11961 //===========================================================================================================================
11962
11963 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
11964 {
11965 OSStatus err;
11966 GAITestCase * obj;
11967
11968 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
11969 require_action( obj, exit, err = kNoMemoryErr );
11970
11971 obj->title = strdup( inTitle );
11972 require_action( obj->title, exit, err = kNoMemoryErr );
11973
11974 *outCase = obj;
11975 obj = NULL;
11976 err = kNoErr;
11977
11978 exit:
11979 if( obj ) GAITestCaseFree( obj );
11980 return( err );
11981 }
11982
11983 //===========================================================================================================================
11984 // GAITestCaseFree
11985 //===========================================================================================================================
11986
11987 static void GAITestCaseFree( GAITestCase *inCase )
11988 {
11989 GAITestItem * item;
11990
11991 while( ( item = inCase->itemList ) != NULL )
11992 {
11993 inCase->itemList = item->next;
11994 GAITestItemFree( item );
11995 }
11996 ForgetMem( &inCase->title );
11997 free( inCase );
11998 }
11999
12000 //===========================================================================================================================
12001 // GAITestCaseAddItem
12002 //===========================================================================================================================
12003
12004 static OSStatus
12005 GAITestCaseAddItem(
12006 GAITestCase * inCase,
12007 unsigned int inAliasCount,
12008 unsigned int inAddressCount,
12009 int inTTL,
12010 GAITestAddrType inHasAddrs,
12011 GAITestAddrType inWantAddrs,
12012 unsigned int inTimeLimitMs,
12013 unsigned int inItemCount )
12014 {
12015 OSStatus err;
12016 GAITestItem * item;
12017 GAITestItem * item2;
12018 GAITestItem * newItemList = NULL;
12019 GAITestItem ** itemPtr;
12020 char * ptr;
12021 char * end;
12022 unsigned int i;
12023 char name[ 64 ];
12024 char tag[ kGAITesterTagStringLen + 1 ];
12025
12026 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
12027
12028 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12029
12030 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
12031 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
12032 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12033
12034 ptr = &name[ 0 ];
12035 end = &name[ countof( name ) ];
12036
12037 // Add Alias label.
12038
12039 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
12040 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
12041
12042 // Add Count label.
12043
12044 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
12045
12046 // Add TTL label.
12047
12048 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
12049
12050 // Add Tag label.
12051
12052 SNPrintF_Add( &ptr, end, "tag-%s.",
12053 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
12054
12055 // Add IPv4 or IPv6 label if necessary.
12056
12057 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
12058 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
12059
12060 // Finally, add the d.test. labels.
12061
12062 SNPrintF_Add( &ptr, end, "d.test." );
12063
12064 // Create item.
12065
12066 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
12067 require_noerr( err, exit );
12068
12069 newItemList = item;
12070 itemPtr = &item->next;
12071
12072 // Create repeat items.
12073
12074 for( i = 1; i < inItemCount; ++i )
12075 {
12076 err = GAITestItemDup( item, &item2 );
12077 require_noerr( err, exit );
12078
12079 *itemPtr = item2;
12080 itemPtr = &item2->next;
12081 }
12082
12083 // Append to test case's item list.
12084
12085 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12086 *itemPtr = newItemList;
12087 newItemList = NULL;
12088
12089 exit:
12090 while( ( item = newItemList ) != NULL )
12091 {
12092 newItemList = item->next;
12093 GAITestItemFree( item );
12094 }
12095 return( err );
12096 }
12097
12098 //===========================================================================================================================
12099 // GAITestCaseAddLocalHostItem
12100 //===========================================================================================================================
12101
12102 static OSStatus
12103 GAITestCaseAddLocalHostItem(
12104 GAITestCase * inCase,
12105 GAITestAddrType inWantAddrs,
12106 unsigned int inTimeLimitMs,
12107 unsigned int inItemCount )
12108 {
12109 OSStatus err;
12110 GAITestItem * item;
12111 GAITestItem * item2;
12112 GAITestItem * newItemList = NULL;
12113 GAITestItem ** itemPtr;
12114 unsigned int i;
12115
12116 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
12117
12118 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
12119 require_noerr( err, exit );
12120
12121 newItemList = item;
12122 itemPtr = &item->next;
12123
12124 // Create repeat items.
12125
12126 for( i = 1; i < inItemCount; ++i )
12127 {
12128 err = GAITestItemDup( item, &item2 );
12129 require_noerr( err, exit );
12130
12131 *itemPtr = item2;
12132 itemPtr = &item2->next;
12133 }
12134
12135 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12136 *itemPtr = newItemList;
12137 newItemList = NULL;
12138
12139 exit:
12140 while( ( item = newItemList ) != NULL )
12141 {
12142 newItemList = item->next;
12143 GAITestItemFree( item );
12144 }
12145 return( err );
12146 }
12147
12148 //===========================================================================================================================
12149 // GAITestItemCreate
12150 //===========================================================================================================================
12151
12152 static OSStatus
12153 GAITestItemCreate(
12154 const char * inName,
12155 unsigned int inAddressCount,
12156 GAITestAddrType inHasAddrs,
12157 GAITestAddrType inWantAddrs,
12158 unsigned int inTimeLimitMs,
12159 GAITestItem ** outItem )
12160 {
12161 OSStatus err;
12162 GAITestItem * obj = NULL;
12163
12164 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
12165 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12166 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
12167
12168 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12169 require_action( obj, exit, err = kNoMemoryErr );
12170
12171 obj->name = strdup( inName );
12172 require_action( obj->name, exit, err = kNoMemoryErr );
12173
12174 obj->addressCount = inAddressCount;
12175 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12176 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12177 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12178 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12179 obj->error = kInProgressErr;
12180 obj->timeLimitMs = inTimeLimitMs;
12181
12182 *outItem = obj;
12183 obj = NULL;
12184 err = kNoErr;
12185
12186 exit:
12187 if( obj ) GAITestItemFree( obj );
12188 return( err );
12189 }
12190
12191 //===========================================================================================================================
12192 // GAITestItemDup
12193 //===========================================================================================================================
12194
12195 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
12196 {
12197 OSStatus err;
12198 GAITestItem * obj;
12199
12200 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12201 require_action( obj, exit, err = kNoMemoryErr );
12202
12203 *obj = *inItem;
12204 obj->next = NULL;
12205 if( inItem->name )
12206 {
12207 obj->name = strdup( inItem->name );
12208 require_action( obj->name, exit, err = kNoMemoryErr );
12209 }
12210
12211 *outItem = obj;
12212 obj = NULL;
12213 err = kNoErr;
12214
12215 exit:
12216 if( obj ) GAITestItemFree( obj );
12217 return( err );
12218 }
12219
12220 //===========================================================================================================================
12221 // GAITestItemFree
12222 //===========================================================================================================================
12223
12224 static void GAITestItemFree( GAITestItem *inItem )
12225 {
12226 ForgetMem( &inItem->name );
12227 free( inItem );
12228 }
12229
12230 //===========================================================================================================================
12231 // MDNSDiscoveryTestCmd
12232 //===========================================================================================================================
12233
12234 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12235
12236 typedef struct
12237 {
12238 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12239 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
12240 NanoTime64 startTime; // When the test started.
12241 NanoTime64 endTime; // When the test ended.
12242 pid_t replierPID; // PID of mDNS replier.
12243 uint32_t ifIndex; // Index of interface to run the replier on.
12244 unsigned int instanceCount; // Desired number of service instances.
12245 unsigned int txtSize; // Desired size of each service instance's TXT record data.
12246 unsigned int recordCountA; // Desired number of A records per replier hostname.
12247 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
12248 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
12249 double ucastDropRate; // Replier's probability of dropping a unicast response.
12250 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
12251 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
12252 Boolean useIPv4; // True if the replier is to use IPv4.
12253 Boolean useIPv6; // True if the replier is to use IPv6.
12254 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
12255 char * replierCommand; // Command used to run the replier.
12256 char * serviceType; // Type of services to browse for.
12257 ServiceBrowserRef browser; // Service browser.
12258 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
12259 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12260 OutputFormatType outputFormat; // Format of test results output.
12261 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
12262 char hostname[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12263 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12264
12265 } MDNSDiscoveryTestContext;
12266
12267 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
12268 static void DNSSD_API
12269 _MDNSDiscoveryTestAboutQueryCallback(
12270 DNSServiceRef inSDRef,
12271 DNSServiceFlags inFlags,
12272 uint32_t inInterfaceIndex,
12273 DNSServiceErrorType inError,
12274 const char * inFullName,
12275 uint16_t inType,
12276 uint16_t inClass,
12277 uint16_t inRDataLen,
12278 const void * inRDataPtr,
12279 uint32_t inTTL,
12280 void * inContext );
12281 static void
12282 _MDNSDiscoveryTestServiceBrowserCallback(
12283 ServiceBrowserResults * inResults,
12284 OSStatus inError,
12285 void * inContext );
12286 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
12287
12288 static void MDNSDiscoveryTestCmd( void )
12289 {
12290 OSStatus err;
12291 MDNSDiscoveryTestContext * context;
12292 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
12293
12294 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
12295 require_action( context, exit, err = kNoMemoryErr );
12296
12297 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
12298 require_noerr_quiet( err, exit );
12299
12300 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
12301 require_noerr_quiet( err, exit );
12302
12303 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
12304 require_noerr_quiet( err, exit );
12305
12306 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
12307 require_noerr_quiet( err, exit );
12308
12309 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
12310 require_noerr_quiet( err, exit );
12311
12312 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
12313 require_noerr_quiet( err, exit );
12314
12315 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
12316 require_noerr_quiet( err, exit );
12317
12318 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
12319 require_noerr_quiet( err, exit );
12320
12321 context->replierPID = -1;
12322 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
12323 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
12324 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
12325 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
12326 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
12327 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
12328 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
12329 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
12330 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
12331 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
12332 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
12333 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
12334 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
12335
12336 if( gMDNSDiscoveryTest_Interface )
12337 {
12338 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
12339 require_noerr_quiet( err, exit );
12340 }
12341 else
12342 {
12343 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
12344 require_noerr_quiet( err, exit );
12345 }
12346
12347 err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
12348 require_noerr_quiet( err, exit );
12349
12350 if( gMDNSDiscoveryTest_FlushCache )
12351 {
12352 err = CheckRootUser();
12353 require_noerr_quiet( err, exit );
12354
12355 err = systemf( NULL, "killall -HUP mDNSResponder" );
12356 require_noerr( err, exit );
12357 sleep( 1 );
12358 context->flushedCache = true;
12359 }
12360
12361 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
12362 context->hostname );
12363 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
12364
12365 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
12366 require_action( context->serviceType, exit, err = kUnknownErr );
12367
12368 ASPrintF( &context->replierCommand,
12369 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12370 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12371 (int64_t) getpid(),
12372 context->ifIndex,
12373 context->hostname,
12374 context->tag,
12375 context->instanceCount,
12376 context->recordCountA,
12377 context->recordCountAAAA,
12378 context->ucastDropRate,
12379 context->mcastDropRate,
12380 context->maxDropCount,
12381 context->noAdditionals, " --noAdditionals",
12382 context->useIPv4, " --ipv4",
12383 context->useIPv6, " --ipv6" );
12384 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
12385
12386 err = SpawnCommand( &context->replierPID, "%s", context->replierCommand );
12387 require_noerr_quiet( err, exit );
12388
12389 // Query for the replier's about TXT record. A response means it's fully up and running.
12390
12391 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
12392 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
12393 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
12394 require_noerr( err, exit );
12395
12396 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
12397 require_noerr( err, exit );
12398
12399 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
12400 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
12401 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
12402 require_noerr( err, exit );
12403 dispatch_resume( context->queryTimer );
12404
12405 context->startTime = NanoTimeGetCurrent();
12406 dispatch_main();
12407
12408 exit:
12409 exit( 1 );
12410 }
12411
12412 //===========================================================================================================================
12413 // _MDNSDiscoveryTestFirstQueryTimeout
12414 //===========================================================================================================================
12415
12416 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
12417 {
12418 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12419
12420 dispatch_source_forget( &context->queryTimer );
12421
12422 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12423 exit( 1 );
12424 }
12425
12426 //===========================================================================================================================
12427 // _MDNSDiscoveryTestAboutQueryCallback
12428 //===========================================================================================================================
12429
12430 static void DNSSD_API
12431 _MDNSDiscoveryTestAboutQueryCallback(
12432 DNSServiceRef inSDRef,
12433 DNSServiceFlags inFlags,
12434 uint32_t inInterfaceIndex,
12435 DNSServiceErrorType inError,
12436 const char * inFullName,
12437 uint16_t inType,
12438 uint16_t inClass,
12439 uint16_t inRDataLen,
12440 const void * inRDataPtr,
12441 uint32_t inTTL,
12442 void * inContext )
12443 {
12444 OSStatus err;
12445 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12446
12447 Unused( inSDRef );
12448 Unused( inInterfaceIndex );
12449 Unused( inFullName );
12450 Unused( inType );
12451 Unused( inClass );
12452 Unused( inRDataLen );
12453 Unused( inRDataPtr );
12454 Unused( inTTL );
12455
12456 err = inError;
12457 require_noerr( err, exit );
12458 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
12459
12460 DNSServiceForget( &context->query );
12461 dispatch_source_forget( &context->queryTimer );
12462
12463 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
12464 require_noerr( err, exit );
12465
12466 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
12467 require_noerr( err, exit );
12468
12469 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
12470 ServiceBrowserStart( context->browser );
12471
12472 exit:
12473 if( err ) exit( 1 );
12474 }
12475
12476 //===========================================================================================================================
12477 // _MDNSDiscoveryTestServiceBrowserCallback
12478 //===========================================================================================================================
12479
12480 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12481 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12482 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12483 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12484 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12485 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12486 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12487 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12488 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12489 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12490 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12491
12492 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12493 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12494 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12495 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12496 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12497 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12498 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12499 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12500 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12501 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12502
12503 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12504 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12505
12506 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12507 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12508 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12509 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12510 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12511 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12512 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12513
12514 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
12515 {
12516 OSStatus err;
12517 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12518 const SBRDomain * domain;
12519 const SBRServiceType * type;
12520 const SBRServiceInstance * instance;
12521 const SBRServiceInstance ** instanceArray = NULL;
12522 const SBRIPAddress * ipaddr;
12523 size_t hostnameLen;
12524 const char * ptr;
12525 const char * end;
12526 unsigned int i;
12527 uint32_t u32;
12528 CFMutableArrayRef unexpectedInstances;
12529 CFMutableArrayRef missingInstances;
12530 CFMutableArrayRef incorrectInstances;
12531 CFMutableDictionaryRef plist = NULL;
12532 CFMutableDictionaryRef badDict = NULL;
12533 CFMutableArrayRef unexpectedAddrs = NULL;
12534 CFMutableArrayRef missingAddrs = NULL;
12535 uint64_t maxResolveTimeUs;
12536 int success = false;
12537 char startTime[ 32 ];
12538 char endTime[ 32 ];
12539
12540 context->endTime = NanoTimeGetCurrent();
12541
12542 err = inError;
12543 require_noerr( err, exit );
12544
12545 _NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
12546 _NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
12547 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
12548 "{"
12549 "%kO="
12550 "{"
12551 "%kO=%s" // replierCommand
12552 "%kO=%lli" // txtSize
12553 "%kO=%lli" // instanceCount
12554 "%kO=%lli" // recordCountA
12555 "%kO=%lli" // recordCountAAAA
12556 "%kO=%s" // hostname
12557 "%kO=%b" // noAdditionals
12558 "%kO=%f" // ucastDropRate
12559 "%kO=%f" // mcastDropRate
12560 "%kO=%i" // maxDropCount
12561 "}"
12562 "%kO=%s" // startTime
12563 "%kO=%s" // endTime
12564 "%kO=%lli" // browseTimeSecs
12565 "%kO=%s" // serviceType
12566 "%kO=%b" // flushedCache
12567 "%kO=[%@]" // unexpectedInstances
12568 "%kO=[%@]" // missingInstances
12569 "%kO=[%@]" // incorrectInstances
12570 "}",
12571 kMDNSDiscoveryTestResultsKey_ReplierInfo,
12572 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
12573 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
12574 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
12575 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
12576 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
12577 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
12578 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
12579 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
12580 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
12581 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
12582 kMDNSDiscoveryTestResultsKey_StartTime, startTime,
12583 kMDNSDiscoveryTestResultsKey_EndTime, endTime,
12584 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
12585 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
12586 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
12587 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
12588 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
12589 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
12590 require_noerr( err, exit );
12591
12592 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
12593 require_action( domain, exit, err = kInternalErr );
12594
12595 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
12596 require_action( type, exit, err = kInternalErr );
12597
12598 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
12599 require_action( instanceArray, exit, err = kNoMemoryErr );
12600
12601 hostnameLen = strlen( context->hostname );
12602 for( instance = type->instanceList; instance; instance = instance->next )
12603 {
12604 unsigned int instanceNumber = 0;
12605
12606 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
12607 {
12608 ptr = &instance->name[ hostnameLen ];
12609 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
12610 {
12611 ptr += 2;
12612 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12613 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12614 {
12615 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
12616 {
12617 instanceNumber = u32;
12618 }
12619 }
12620 }
12621 else if( *ptr == '\0' )
12622 {
12623 instanceNumber = 1;
12624 }
12625 }
12626 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
12627 {
12628 check( !instanceArray[ instanceNumber - 1 ] );
12629 instanceArray[ instanceNumber - 1 ] = instance;
12630 }
12631 else
12632 {
12633 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
12634 "{"
12635 "%kO=%s"
12636 "%kO=%lli"
12637 "}",
12638 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
12639 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
12640 require_noerr( err, exit );
12641 }
12642 }
12643
12644 maxResolveTimeUs = 0;
12645 for( i = 1; i <= context->instanceCount; ++i )
12646 {
12647 int isHostnameValid;
12648 int isTXTValid;
12649
12650 instance = instanceArray[ i - 1 ];
12651 if( !instance )
12652 {
12653 if( i == 1 )
12654 {
12655 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
12656 require_noerr( err, exit );
12657 }
12658 else
12659 {
12660 char * instanceName = NULL;
12661
12662 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
12663 require_action( instanceName, exit, err = kUnknownErr );
12664
12665 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
12666 free( instanceName );
12667 require_noerr( err, exit );
12668 }
12669 continue;
12670 }
12671
12672 if( !instance->hostname )
12673 {
12674 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
12675 "{"
12676 "%kO=%s"
12677 "%kO=%b"
12678 "}",
12679 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12680 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
12681 require_noerr( err, exit );
12682 continue;
12683 }
12684
12685 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
12686 require_action( badDict, exit, err = kNoMemoryErr );
12687
12688 isHostnameValid = false;
12689 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
12690 {
12691 ptr = &instance->hostname[ hostnameLen ];
12692 if( i == 1 )
12693 {
12694 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
12695 }
12696 else if( *ptr == '-' )
12697 {
12698 ++ptr;
12699 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12700 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12701 {
12702 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
12703 }
12704 }
12705 }
12706 if( !isHostnameValid )
12707 {
12708 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
12709 kSizeCString );
12710 require_noerr( err, exit );
12711 }
12712
12713 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
12714 {
12715 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
12716 require_noerr( err, exit );
12717 }
12718
12719 isTXTValid = false;
12720 if( instance->txtLen == context->txtSize )
12721 {
12722 uint8_t name[ kDomainNameLengthMax ];
12723
12724 err = DomainNameFromString( name, instance->name, NULL );
12725 require_noerr( err, exit );
12726
12727 err = DomainNameAppendString( name, type->name, NULL );
12728 require_noerr( err, exit );
12729
12730 err = DomainNameAppendString( name, "local", NULL );
12731 require_noerr( err, exit );
12732
12733 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
12734 }
12735 if( !isTXTValid )
12736 {
12737 char * hexStr = NULL;
12738
12739 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
12740 require_action( hexStr, exit, err = kUnknownErr );
12741
12742 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
12743 free( hexStr );
12744 require_noerr( err, exit );
12745 }
12746
12747 if( isHostnameValid )
12748 {
12749 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
12750 unsigned int j;
12751 uint8_t addrV4[ 4 ];
12752 uint8_t addrV6[ 16 ];
12753
12754 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
12755 else addrV4Bitmap = ~UINT64_C( 0 );
12756
12757 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
12758 else addrV6Bitmap = ~UINT64_C( 0 );
12759
12760 addrV4[ 0 ] = 0;
12761 WriteBig16( &addrV4[ 1 ], i );
12762 addrV4[ 3 ] = 0;
12763
12764 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
12765 WriteBig16( &addrV6[ 12 ], i );
12766
12767 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12768 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
12769
12770 resolveTimeUs = 0;
12771 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
12772 {
12773 const uint8_t * addrPtr;
12774 unsigned int lsb;
12775 int isAddrValid = false;
12776
12777 if( ipaddr->sip.sa.sa_family == AF_INET )
12778 {
12779 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
12780 lsb = addrPtr[ 3 ];
12781 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
12782 {
12783 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12784 addrV4Bitmap &= ~bitmask;
12785 isAddrValid = true;
12786 }
12787 }
12788 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
12789 {
12790 addrPtr = ipaddr->sip.v6.sin6_addr.s6_addr;
12791 lsb = addrPtr[ 15 ];
12792 if( ( memcmp( addrPtr, addrV6, 15 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
12793 {
12794 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12795 addrV6Bitmap &= ~bitmask;
12796 isAddrValid = true;
12797 }
12798 }
12799 if( isAddrValid )
12800 {
12801 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
12802 }
12803 else
12804 {
12805 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
12806 require_noerr( err, exit );
12807 }
12808 }
12809
12810 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
12811 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
12812
12813 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
12814 {
12815 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
12816 }
12817 ForgetCF( &unexpectedAddrs );
12818
12819 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12820 require_action( missingAddrs, exit, err = kNoMemoryErr );
12821
12822 for( j = 1; addrV4Bitmap != 0; ++j )
12823 {
12824 bitmask = UINT64_C( 1 ) << ( j - 1 );
12825 if( addrV4Bitmap & bitmask )
12826 {
12827 addrV4Bitmap &= ~bitmask;
12828 addrV4[ 3 ] = (uint8_t) j;
12829 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
12830 require_noerr( err, exit );
12831 }
12832 }
12833 for( j = 1; addrV6Bitmap != 0; ++j )
12834 {
12835 bitmask = UINT64_C( 1 ) << ( j - 1 );
12836 if( addrV6Bitmap & bitmask )
12837 {
12838 addrV6Bitmap &= ~bitmask;
12839 addrV6[ 15 ] = (uint8_t) j;
12840 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.16a", addrV6 );
12841 require_noerr( err, exit );
12842 }
12843 }
12844
12845 if( CFArrayGetCount( missingAddrs ) > 0 )
12846 {
12847 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
12848 }
12849 ForgetCF( &missingAddrs );
12850 }
12851
12852 if( CFDictionaryGetCount( badDict ) > 0 )
12853 {
12854 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12855 kSizeCString );
12856 require_noerr( err, exit );
12857
12858 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
12859 CFArrayAppendValue( incorrectInstances, badDict );
12860 }
12861 ForgetCF( &badDict );
12862 }
12863
12864 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
12865 ( CFArrayGetCount( missingInstances ) == 0 ) &&
12866 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
12867 {
12868 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
12869 require_noerr( err, exit );
12870 success = true;
12871 }
12872 else
12873 {
12874 success = false;
12875 }
12876 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
12877
12878 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
12879 require_noerr_quiet( err, exit );
12880
12881 exit:
12882 ForgetCF( &context->browser );
12883 if( context->replierPID != -1 )
12884 {
12885 kill( context->replierPID, SIGTERM );
12886 context->replierPID = -1;
12887 }
12888 FreeNullSafe( instanceArray );
12889 CFReleaseNullSafe( plist );
12890 CFReleaseNullSafe( badDict );
12891 CFReleaseNullSafe( unexpectedAddrs );
12892 CFReleaseNullSafe( missingAddrs );
12893 exit( err ? 1 : ( success ? 0 : 2 ) );
12894 }
12895
12896 //===========================================================================================================================
12897 // _MDNSDiscoveryTestTXTRecordIsValid
12898 //===========================================================================================================================
12899
12900 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
12901 {
12902 uint32_t hash;
12903 int n;
12904 const uint8_t * ptr;
12905 size_t i, wholeCount, remCount;
12906 uint8_t txtStr[ 16 ];
12907
12908 if( inTXTLen == 0 ) return( false );
12909
12910 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
12911
12912 txtStr[ 0 ] = 15;
12913 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12914 check( n == 15 );
12915
12916 ptr = inTXTPtr;
12917 wholeCount = inTXTLen / 16;
12918 for( i = 0; i < wholeCount; ++i )
12919 {
12920 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
12921 ptr += 16;
12922 }
12923
12924 remCount = inTXTLen % 16;
12925 if( remCount > 0 )
12926 {
12927 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12928 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
12929 ptr += remCount;
12930 }
12931 check( ptr == &inTXTPtr[ inTXTLen ] );
12932 return( true );
12933 }
12934
12935 //===========================================================================================================================
12936 // DotLocalTestCmd
12937 //===========================================================================================================================
12938
12939 #define kDotLocalTestPreparationTimeLimitSecs 5
12940 #define kDotLocalTestSubtestDurationSecs 5
12941
12942 // Constants for SRV record query subtest.
12943
12944 #define kDotLocalTestSRV_Priority 1
12945 #define kDotLocalTestSRV_Weight 0
12946 #define kDotLocalTestSRV_Port 80
12947 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12948 #define kDotLocalTestSRV_TargetStr "www.example.com."
12949 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12950
12951 typedef enum
12952 {
12953 kDotLocalTestState_Unset = 0,
12954 kDotLocalTestState_Preparing = 1,
12955 kDotLocalTestState_GAIMDNSOnly = 2,
12956 kDotLocalTestState_GAIDNSOnly = 3,
12957 kDotLocalTestState_GAIBoth = 4,
12958 kDotLocalTestState_GAINeither = 5,
12959 kDotLocalTestState_GAINoSuchRecord = 6,
12960 kDotLocalTestState_QuerySRV = 7,
12961 kDotLocalTestState_Done = 8
12962
12963 } DotLocalTestState;
12964
12965 typedef struct
12966 {
12967 const char * testDesc; // Description of the current subtest.
12968 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
12969 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
12970 NanoTime64 startTime; // Timestamp of when the subtest started.
12971 NanoTime64 endTime; // Timestamp of when the subtest ended.
12972 CFMutableArrayRef correctResults; // Operation results that were expected.
12973 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
12974 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
12975 OSStatus error; // Subtest's error code.
12976 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12977 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12978 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12979 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12980 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
12981 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
12982 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
12983 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
12984 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
12985 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
12986 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
12987 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
12988 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
12989
12990 } DotLocalSubtest;
12991
12992 typedef struct
12993 {
12994 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
12995 DotLocalSubtest * subtest; // Current subtest's state.
12996 DNSServiceRef connection; // Shared connection for DNS-SD operations.
12997 DNSServiceRef op; // Reference for the current DNS-SD operation.
12998 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
12999 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13000 char * replierCmd; // Command used to invoke the mdnsreplier.
13001 char * serverCmd; // Command used to invoke the test DNS server.
13002 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
13003 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13004 NanoTime64 startTime; // Timestamp for when the test started.
13005 NanoTime64 endTime; // Timestamp for when the test ended.
13006 DotLocalTestState state; // The test's current state.
13007 pid_t replierPID; // PID of spawned mdnsreplier.
13008 pid_t serverPID; // PID of spawned test DNS server.
13009 uint32_t ifIndex; // Interface index used for mdnsreplier.
13010 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
13011 OutputFormatType outputFormat; // Format of test results output.
13012 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
13013 Boolean serverIsReady; // True if response was received for test DNS server probe query.
13014 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
13015 Boolean testFailed; // True if at least one subtest failed.
13016 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13017 // The format of this string is "dotlocal-test-<six random chars>".
13018 } DotLocalTestContext;
13019
13020 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
13021 static void DNSSD_API
13022 _DotLocalTestProbeQueryRecordCallback(
13023 DNSServiceRef inSDRef,
13024 DNSServiceFlags inFlags,
13025 uint32_t inInterfaceIndex,
13026 DNSServiceErrorType inError,
13027 const char * inFullName,
13028 uint16_t inType,
13029 uint16_t inClass,
13030 uint16_t inRDataLen,
13031 const void * inRDataPtr,
13032 uint32_t inTTL,
13033 void * inContext );
13034 static void DNSSD_API
13035 _DotLocalTestRegisterRecordCallback(
13036 DNSServiceRef inSDRef,
13037 DNSRecordRef inRecordRef,
13038 DNSServiceFlags inFlags,
13039 DNSServiceErrorType inError,
13040 void * inContext );
13041 static void _DotLocalTestTimerHandler( void *inContext );
13042 static void DNSSD_API
13043 _DotLocalTestGAICallback(
13044 DNSServiceRef inSDRef,
13045 DNSServiceFlags inFlags,
13046 uint32_t inInterfaceIndex,
13047 DNSServiceErrorType inError,
13048 const char * inHostname,
13049 const struct sockaddr * inSockAddr,
13050 uint32_t inTTL,
13051 void * inContext );
13052 static void DNSSD_API
13053 _DotLocalTestQueryRecordCallback(
13054 DNSServiceRef inSDRef,
13055 DNSServiceFlags inFlags,
13056 uint32_t inInterfaceIndex,
13057 DNSServiceErrorType inError,
13058 const char * inFullName,
13059 uint16_t inType,
13060 uint16_t inClass,
13061 uint16_t inRDataLen,
13062 const void * inRDataPtr,
13063 uint32_t inTTL,
13064 void * inContext );
13065
13066 static void DotLocalTestCmd( void )
13067 {
13068 OSStatus err;
13069 DotLocalTestContext * context;
13070 uint8_t * rdataPtr;
13071 size_t rdataLen;
13072 DNSServiceFlags flags;
13073 char queryName[ 64 ];
13074 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
13075
13076 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
13077 require_action( context, exit, err = kNoMemoryErr );
13078
13079 context->startTime = NanoTimeGetCurrent();
13080 context->endTime = kNanoTime_Invalid;
13081
13082 context->state = kDotLocalTestState_Preparing;
13083
13084 if( gDotLocalTest_Interface )
13085 {
13086 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
13087 require_noerr_quiet( err, exit );
13088 }
13089 else
13090 {
13091 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
13092 require_noerr_quiet( err, exit );
13093 }
13094
13095 if( gDotLocalTest_OutputFilePath )
13096 {
13097 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
13098 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
13099 }
13100
13101 err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
13102 require_noerr_quiet( err, exit );
13103
13104 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13105 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
13106
13107 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13108 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
13109
13110 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
13111 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
13112
13113 // Spawn an mdnsreplier.
13114
13115 ASPrintF( &context->replierCmd,
13116 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13117 " --countAAAA 1",
13118 (int64_t) getpid(), context->ifIndex, context->labelStr,
13119 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
13120 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
13121
13122 err = SpawnCommand( &context->replierPID, "%s", context->replierCmd );
13123 require_noerr( err, exit );
13124
13125 // Spawn a test DNS server
13126
13127 ASPrintF( &context->serverCmd,
13128 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13129 (int64_t) getpid(), context->labelStr );
13130 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
13131
13132 err = SpawnCommand( &context->serverPID, "%s", context->serverCmd );
13133 require_noerr( err, exit );
13134
13135 // Create a shared DNS-SD connection.
13136
13137 err = DNSServiceCreateConnection( &context->connection );
13138 require_noerr( err, exit );
13139
13140 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
13141 require_noerr( err, exit );
13142
13143 // Create probe query for DNS server, i.e., query for any name that has an A record.
13144
13145 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
13146
13147 flags = kDNSServiceFlagsShareConnection;
13148 #if( TARGET_OS_WATCH )
13149 flags |= kDNSServiceFlagsPathEvaluationDone;
13150 #endif
13151
13152 context->op = context->connection;
13153 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
13154 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
13155 require_noerr( err, exit );
13156
13157 // Create probe query for mdnsreplier's "about" TXT record.
13158
13159 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
13160
13161 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
13162 #if( TARGET_OS_WATCH )
13163 flags |= kDNSServiceFlagsPathEvaluationDone;
13164 #endif
13165
13166 context->op2 = context->connection;
13167 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
13168 _DotLocalTestProbeQueryRecordCallback, context );
13169 require_noerr( err, exit );
13170
13171 // Register a dummy local. SOA record.
13172
13173 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
13174 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
13175 require_noerr( err, exit );
13176
13177 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
13178 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN, 1,
13179 rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
13180 require_noerr( err, exit );
13181
13182 // Start timer for probe responses and SOA record registration.
13183
13184 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
13185 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13186 _DotLocalTestTimerHandler, context, &context->timer );
13187 require_noerr( err, exit );
13188 dispatch_resume( context->timer );
13189
13190 dispatch_main();
13191
13192 exit:
13193 if( err ) ErrQuit( 1, "error: %#m\n", err );
13194 }
13195
13196 //===========================================================================================================================
13197 // _DotLocalTestStateMachine
13198 //===========================================================================================================================
13199
13200 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
13201 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
13202 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
13203 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
13204 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
13205
13206 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
13207 {
13208 OSStatus err;
13209 DotLocalTestState nextState;
13210
13211 DNSServiceForget( &inContext->op );
13212 DNSServiceForget( &inContext->op2 );
13213 dispatch_source_forget( &inContext->timer );
13214
13215 switch( inContext->state )
13216 {
13217 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
13218 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
13219 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
13220 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
13221 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
13222 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
13223 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
13224 default: err = kStateErr; goto exit;
13225 }
13226
13227 if( inContext->state == kDotLocalTestState_Preparing )
13228 {
13229 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
13230 {
13231 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13232 YesNoStr( inContext->registeredSOA ),
13233 YesNoStr( inContext->serverIsReady ),
13234 YesNoStr( inContext->replierIsReady ) );
13235 err = kNotPreparedErr;
13236 goto exit;
13237 }
13238 }
13239 else
13240 {
13241 err = _DotLocalTestFinalizeSubtest( inContext );
13242 require_noerr( err, exit );
13243 }
13244
13245 inContext->state = nextState;
13246 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
13247 err = _DotLocalTestStartSubtest( inContext );
13248
13249 exit:
13250 if( err ) ErrQuit( 1, "error: %#m\n", err );
13251 }
13252
13253 //===========================================================================================================================
13254 // _DotLocalSubtestCreate
13255 //===========================================================================================================================
13256
13257 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
13258 {
13259 OSStatus err;
13260 DotLocalSubtest * obj;
13261
13262 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
13263 require_action( obj, exit, err = kNoMemoryErr );
13264
13265 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13266 require_action( obj->correctResults, exit, err = kNoMemoryErr );
13267
13268 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13269 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
13270
13271 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13272 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
13273
13274 *outSubtest = obj;
13275 obj = NULL;
13276 err = kNoErr;
13277
13278 exit:
13279 if( obj ) _DotLocalSubtestFree( obj );
13280 return( err );
13281 }
13282
13283 //===========================================================================================================================
13284 // _DotLocalSubtestFree
13285 //===========================================================================================================================
13286
13287 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
13288 {
13289 ForgetMem( &inSubtest->queryName );
13290 ForgetCF( &inSubtest->correctResults );
13291 ForgetCF( &inSubtest->duplicateResults );
13292 ForgetCF( &inSubtest->unexpectedResults );
13293 free( inSubtest );
13294 }
13295
13296 //===========================================================================================================================
13297 // _DotLocalTestStartSubtest
13298 //===========================================================================================================================
13299
13300 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
13301 {
13302 OSStatus err;
13303 DotLocalSubtest * subtest = NULL;
13304 DNSServiceRef op = NULL;
13305 DNSServiceFlags flags;
13306
13307 err = _DotLocalSubtestCreate( &subtest );
13308 require_noerr( err, exit );
13309
13310 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
13311 {
13312 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
13313 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13314
13315 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13316 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13317
13318 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
13319 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::2:1
13320 subtest->addrMDNSv6[ 13 ] = 2;
13321 subtest->addrMDNSv6[ 15 ] = 1;
13322
13323 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
13324 }
13325
13326 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
13327 {
13328 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
13329 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13330
13331 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13332 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13333
13334 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13335 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13336 subtest->addrDNSv6[ 15 ] = 1;
13337
13338 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
13339 }
13340
13341 else if( inContext->state == kDotLocalTestState_GAIBoth )
13342 {
13343 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
13344 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13345
13346 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13347 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13348 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13349 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13350
13351 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13352 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13353 subtest->addrDNSv6[ 15 ] = 1;
13354
13355 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
13356 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::1:1
13357 subtest->addrMDNSv6[ 13 ] = 1;
13358 subtest->addrMDNSv6[ 15 ] = 1;
13359
13360 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
13361 }
13362
13363 else if( inContext->state == kDotLocalTestState_GAINeither )
13364 {
13365 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
13366 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13367
13368 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
13369 }
13370
13371 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
13372 {
13373 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
13374 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13375
13376 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13377 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13378 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
13379 }
13380
13381 else if( inContext->state == kDotLocalTestState_QuerySRV )
13382 {
13383 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13384 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
13385 inContext->labelStr );
13386 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13387
13388 subtest->needSRV = true;
13389 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
13390 }
13391
13392 else
13393 {
13394 err = kStateErr;
13395 goto exit;
13396 }
13397
13398 // Start new operation.
13399
13400 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
13401 #if( TARGET_OS_WATCH )
13402 flags |= kDNSServiceFlagsPathEvaluationDone;
13403 #endif
13404
13405 subtest->startTime = NanoTimeGetCurrent();
13406 subtest->endTime = kNanoTime_Invalid;
13407
13408 if( inContext->state == kDotLocalTestState_QuerySRV )
13409 {
13410 op = inContext->connection;
13411 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
13412 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
13413 require_noerr( err, exit );
13414 }
13415 else
13416 {
13417 op = inContext->connection;
13418 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
13419 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
13420 require_noerr( err, exit );
13421 }
13422
13423 // Start timer.
13424
13425 check( !inContext->timer );
13426 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
13427 INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13428 _DotLocalTestTimerHandler, inContext, &inContext->timer );
13429 require_noerr( err, exit );
13430 dispatch_resume( inContext->timer );
13431
13432 check( !inContext->op );
13433 inContext->op = op;
13434 op = NULL;
13435
13436 check( !inContext->subtest );
13437 inContext->subtest = subtest;
13438 subtest = NULL;
13439
13440 exit:
13441 if( subtest ) _DotLocalSubtestFree( subtest );
13442 if( op ) DNSServiceRefDeallocate( op );
13443 return( err );
13444 }
13445
13446 //===========================================================================================================================
13447 // _DotLocalTestFinalizeSubtest
13448 //===========================================================================================================================
13449
13450 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13451 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13452 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13453 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13454 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13455 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13456 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13457 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13458 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13459 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13460 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13461 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13462 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13463 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13464 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13465
13466 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
13467 {
13468 OSStatus err;
13469 DotLocalSubtest * subtest;
13470 CFMutableDictionaryRef reportDict;
13471 CFMutableDictionaryRef resultsDict;
13472 CFMutableArrayRef missingResults, reportArray;
13473 char startTime[ 32 ];
13474 char endTime[ 32 ];
13475
13476 subtest = inContext->subtest;
13477 inContext->subtest = NULL;
13478
13479 subtest->endTime = NanoTimeGetCurrent();
13480 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
13481 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
13482
13483 reportDict = NULL;
13484 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
13485 "{"
13486 "%kO=%s" // startTime
13487 "%kO=%s" // endTime
13488 "%kO=%s" // queryName
13489 "%kO=%s" // description
13490 "%kO={%@}" // results
13491 "}",
13492 kDotLocalTestReportKey_StartTime, startTime,
13493 kDotLocalTestReportKey_EndTime, endTime,
13494 kDotLocalTestReportKey_QueryName, subtest->queryName,
13495 kDotLocalTestReportKey_Description, subtest->testDesc,
13496 kDotLocalTestReportKey_Results, &resultsDict );
13497 require_noerr( err, exit );
13498
13499 missingResults = NULL;
13500 switch( inContext->state )
13501 {
13502 case kDotLocalTestState_GAIMDNSOnly:
13503 case kDotLocalTestState_GAIDNSOnly:
13504 case kDotLocalTestState_GAIBoth:
13505 case kDotLocalTestState_GAINeither:
13506 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
13507 {
13508 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13509 "["
13510 "%.4a" // Expected DNS IPv4 address
13511 "%.16a" // Expected DNS IPv6 address
13512 "%.4a" // Expected MDNS IPv4 address
13513 "%.16a" // Expected MDNS IPv6 address
13514 "]",
13515 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
13516 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
13517 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
13518 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
13519 require_noerr( err, exit );
13520 }
13521 break;
13522
13523 case kDotLocalTestState_QuerySRV:
13524 if( subtest->needSRV )
13525 {
13526 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13527 "["
13528 "%s" // Expected SRV record data as a string.
13529 "]",
13530 kDotLocalTestSRV_ResultStr );
13531 require_noerr( err, exit );
13532 }
13533 break;
13534
13535 case kDotLocalTestState_GAINoSuchRecord:
13536 if( subtest->needDNSv4 || subtest->needDNSv6 )
13537 {
13538 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13539 "["
13540 "%s" // No Such Record (A)
13541 "%s" // No Such Record (AAAA)
13542 "]",
13543 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
13544 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
13545 require_noerr( err, exit );
13546 }
13547 break;
13548
13549 default:
13550 err = kStateErr;
13551 goto exit;
13552 }
13553
13554 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
13555
13556 if( missingResults )
13557 {
13558 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
13559 ForgetCF( &missingResults );
13560 if( !subtest->error ) subtest->error = kNotFoundErr;
13561 }
13562
13563 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
13564 {
13565 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
13566 if( !subtest->error ) subtest->error = kUnexpectedErr;
13567 }
13568
13569 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
13570 {
13571 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
13572 if( !subtest->error ) subtest->error = kDuplicateErr;
13573 }
13574
13575 if( subtest->error ) inContext->testFailed = true;
13576 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
13577 require_noerr( err, exit );
13578
13579 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
13580 CFArrayAppendValue( reportArray, reportDict );
13581
13582 exit:
13583 _DotLocalSubtestFree( subtest );
13584 CFReleaseNullSafe( reportDict );
13585 return( err );
13586 }
13587
13588 //===========================================================================================================================
13589 // _DotLocalTestFinalizeAndExit
13590 //===========================================================================================================================
13591
13592 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
13593 {
13594 OSStatus err;
13595 CFPropertyListRef plist;
13596 char timestampStart[ 32 ];
13597 char timestampEnd[ 32 ];
13598
13599 check( !inContext->subtest );
13600 inContext->endTime = NanoTimeGetCurrent();
13601
13602 if( inContext->replierPID != -1 )
13603 {
13604 kill( inContext->replierPID, SIGTERM );
13605 inContext->replierPID = -1;
13606 }
13607 if( inContext->serverPID != -1 )
13608 {
13609 kill( inContext->serverPID, SIGTERM );
13610 inContext->serverPID = -1;
13611 }
13612 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
13613 require_noerr( err, exit );
13614
13615 _NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
13616 _NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
13617
13618 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13619 "{"
13620 "%kO=%s" // startTime
13621 "%kO=%s" // endTime
13622 "%kO=%O" // testsGAI
13623 "%kO=%O" // testsQuerySRV
13624 "%kO=%b" // success
13625 "%kO=%s" // replierCmd
13626 "%kO=%s" // serverCmd
13627 "}",
13628 kDotLocalTestReportKey_StartTime, timestampStart,
13629 kDotLocalTestReportKey_EndTime, timestampEnd,
13630 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
13631 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
13632 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
13633 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
13634 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
13635 require_noerr( err, exit );
13636
13637 ForgetCF( &inContext->reportsGAI );
13638 ForgetCF( &inContext->reportsQuerySRV );
13639
13640 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
13641 CFRelease( plist );
13642 require_noerr( err, exit );
13643
13644 exit( inContext->testFailed ? 2 : 0 );
13645
13646 exit:
13647 ErrQuit( 1, "error: %#m\n", err );
13648 }
13649
13650 //===========================================================================================================================
13651 // _DotLocalTestProbeQueryRecordCallback
13652 //===========================================================================================================================
13653
13654 static void DNSSD_API
13655 _DotLocalTestProbeQueryRecordCallback(
13656 DNSServiceRef inSDRef,
13657 DNSServiceFlags inFlags,
13658 uint32_t inInterfaceIndex,
13659 DNSServiceErrorType inError,
13660 const char * inFullName,
13661 uint16_t inType,
13662 uint16_t inClass,
13663 uint16_t inRDataLen,
13664 const void * inRDataPtr,
13665 uint32_t inTTL,
13666 void * inContext )
13667 {
13668 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13669
13670 Unused( inInterfaceIndex );
13671 Unused( inFullName );
13672 Unused( inType );
13673 Unused( inClass );
13674 Unused( inRDataLen );
13675 Unused( inRDataPtr );
13676 Unused( inTTL );
13677
13678 check( context->state == kDotLocalTestState_Preparing );
13679
13680 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
13681
13682 if( inSDRef == context->op )
13683 {
13684 DNSServiceForget( &context->op );
13685 context->serverIsReady = true;
13686 }
13687 else if( inSDRef == context->op2 )
13688 {
13689 DNSServiceForget( &context->op2 );
13690 context->replierIsReady = true;
13691 }
13692
13693 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
13694 {
13695 _DotLocalTestStateMachine( context );
13696 }
13697
13698 exit:
13699 return;
13700 }
13701
13702 //===========================================================================================================================
13703 // _DotLocalTestRegisterRecordCallback
13704 //===========================================================================================================================
13705
13706 static void DNSSD_API
13707 _DotLocalTestRegisterRecordCallback(
13708 DNSServiceRef inSDRef,
13709 DNSRecordRef inRecordRef,
13710 DNSServiceFlags inFlags,
13711 DNSServiceErrorType inError,
13712 void * inContext )
13713 {
13714 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13715
13716 Unused( inSDRef );
13717 Unused( inRecordRef );
13718 Unused( inFlags );
13719
13720 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
13721
13722 if( !context->registeredSOA )
13723 {
13724 context->registeredSOA = true;
13725 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
13726 }
13727 }
13728
13729 //===========================================================================================================================
13730 // _DotLocalTestTimerHandler
13731 //===========================================================================================================================
13732
13733 static void _DotLocalTestTimerHandler( void *inContext )
13734 {
13735 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
13736 }
13737
13738 //===========================================================================================================================
13739 // _DotLocalTestGAICallback
13740 //===========================================================================================================================
13741
13742 static void DNSSD_API
13743 _DotLocalTestGAICallback(
13744 DNSServiceRef inSDRef,
13745 DNSServiceFlags inFlags,
13746 uint32_t inInterfaceIndex,
13747 DNSServiceErrorType inError,
13748 const char * inHostname,
13749 const struct sockaddr * inSockAddr,
13750 uint32_t inTTL,
13751 void * inContext )
13752 {
13753 OSStatus err;
13754 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13755 DotLocalSubtest * const subtest = context->subtest;
13756 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
13757
13758 Unused( inSDRef );
13759 Unused( inInterfaceIndex );
13760 Unused( inHostname );
13761 Unused( inTTL );
13762
13763 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13764 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
13765
13766 if( context->state == kDotLocalTestState_GAINoSuchRecord )
13767 {
13768 if( inError == kDNSServiceErr_NoSuchRecord )
13769 {
13770 CFMutableArrayRef array = NULL;
13771 const char * noSuchRecordStr;
13772
13773 if( sip->sa.sa_family == AF_INET )
13774 {
13775 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13776 subtest->needDNSv4 = false;
13777
13778 noSuchRecordStr = kNoSuchRecordAStr;
13779 }
13780 else
13781 {
13782 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13783 subtest->needDNSv6 = false;
13784
13785 noSuchRecordStr = kNoSuchRecordAAAAStr;
13786 }
13787 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
13788 require_noerr( err, fatal );
13789 }
13790 else if( !inError )
13791 {
13792 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
13793 require_noerr( err, fatal );
13794 }
13795 else
13796 {
13797 err = inError;
13798 goto exit;
13799 }
13800 }
13801 else
13802 {
13803 if( !inError )
13804 {
13805 CFMutableArrayRef array = NULL;
13806
13807 if( sip->sa.sa_family == AF_INET )
13808 {
13809 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
13810
13811 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
13812 {
13813 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13814 subtest->needDNSv4 = false;
13815 }
13816 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
13817 {
13818 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13819 subtest->needMDNSv4 = false;
13820 }
13821 }
13822 else
13823 {
13824 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
13825
13826 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
13827 {
13828 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13829 subtest->needDNSv6 = false;
13830 }
13831 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
13832 {
13833 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13834 subtest->needMDNSv6 = false;
13835 }
13836 }
13837 if( !array ) array = subtest->unexpectedResults;
13838 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
13839 require_noerr( err, fatal );
13840 }
13841 else if( inError == kDNSServiceErr_NoSuchRecord )
13842 {
13843 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
13844 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
13845 require_noerr( err, fatal );
13846 }
13847 else
13848 {
13849 err = inError;
13850 goto exit;
13851 }
13852 }
13853
13854 exit:
13855 if( err )
13856 {
13857 subtest->error = err;
13858 _DotLocalTestStateMachine( context );
13859 }
13860 return;
13861
13862 fatal:
13863 ErrQuit( 1, "error: %#m\n", err );
13864 }
13865
13866 //===========================================================================================================================
13867 // _DotLocalTestQueryRecordCallback
13868 //===========================================================================================================================
13869
13870 static void DNSSD_API
13871 _DotLocalTestQueryRecordCallback(
13872 DNSServiceRef inSDRef,
13873 DNSServiceFlags inFlags,
13874 uint32_t inInterfaceIndex,
13875 DNSServiceErrorType inError,
13876 const char * inFullName,
13877 uint16_t inType,
13878 uint16_t inClass,
13879 uint16_t inRDataLen,
13880 const void * inRDataPtr,
13881 uint32_t inTTL,
13882 void * inContext )
13883 {
13884 OSStatus err;
13885 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13886 DotLocalSubtest * const subtest = context->subtest;
13887 const dns_fixed_fields_srv * fields;
13888 const uint8_t * target;
13889 const uint8_t * ptr;
13890 const uint8_t * end;
13891 char * rdataStr;
13892 unsigned int priority, weight, port;
13893 CFMutableArrayRef array;
13894
13895 Unused( inSDRef );
13896 Unused( inInterfaceIndex );
13897 Unused( inFullName );
13898 Unused( inTTL );
13899
13900 check( context->state == kDotLocalTestState_QuerySRV );
13901
13902 err = inError;
13903 require_noerr_quiet( err, exit );
13904 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13905 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
13906 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
13907
13908 fields = (const dns_fixed_fields_srv *) inRDataPtr;
13909 priority = dns_fixed_fields_srv_get_priority( fields );
13910 weight = dns_fixed_fields_srv_get_weight( fields );
13911 port = dns_fixed_fields_srv_get_port( fields );
13912 target = (const uint8_t *) &fields[ 1 ];
13913 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
13914 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
13915
13916 if( ( priority == kDotLocalTestSRV_Priority ) &&
13917 ( weight == kDotLocalTestSRV_Weight ) &&
13918 ( port == kDotLocalTestSRV_Port ) &&
13919 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
13920 {
13921 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
13922 subtest->needSRV = false;
13923 }
13924 else
13925 {
13926 array = subtest->unexpectedResults;
13927 }
13928
13929 rdataStr = NULL;
13930 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, NULL, 0, &rdataStr );
13931 if( !rdataStr )
13932 {
13933 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
13934 require_action( rdataStr, fatal, err = kNoMemoryErr );
13935 }
13936
13937 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
13938 free( rdataStr );
13939 require_noerr( err, fatal );
13940
13941 exit:
13942 if( err )
13943 {
13944 subtest->error = err;
13945 _DotLocalTestStateMachine( context );
13946 }
13947 return;
13948
13949 fatal:
13950 ErrQuit( 1, "error: %#m\n", err );
13951 }
13952
13953 //===========================================================================================================================
13954 // ProbeConflictTestCmd
13955 //===========================================================================================================================
13956
13957 #define kProbeConflictTestService_DefaultName "pctest-name"
13958 #define kProbeConflictTestService_Port 60000
13959
13960 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13961 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13962
13963 typedef struct
13964 {
13965 const char * description;
13966 const char * program;
13967 Boolean expectsRename;
13968
13969 } ProbeConflictTestCase;
13970
13971 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13972 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13973 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13974
13975 static const ProbeConflictTestCase kProbeConflictTestCases[] =
13976 {
13977 // No conflicts
13978
13979 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
13980
13981 // One multicast probe conflict
13982
13983 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
13984 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
13985 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
13986
13987 // One unicast probe conflict
13988
13989 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
13990 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
13991 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
13992
13993 // One multicast and one unicast probe conflict
13994
13995 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
13996 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
13997 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
13998 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
13999 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
14000 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14001 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
14002 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
14003 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14004
14005 // Two multicast probe conflicts
14006
14007 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
14008 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
14009 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
14010 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
14011 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
14012 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14013 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
14014 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
14015 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14016 };
14017
14018 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14019
14020 typedef struct
14021 {
14022 DNSServiceRef registration; // Test service registration.
14023 NanoTime64 testStartTime; // Test's start time.
14024 NanoTime64 startTime; // Current test case's start time.
14025 MDNSColliderRef collider; // mDNS collider object.
14026 CFMutableArrayRef results; // Array of test case results.
14027 char * serviceName; // Test service's instance name as a string. (malloced)
14028 char * serviceType; // Test service's service type as a string. (malloced)
14029 uint8_t * recordName; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14030 unsigned int testCaseIndex; // Index of the current test case.
14031 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
14032 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14033 OutputFormatType outputFormat; // Format of test report output.
14034 Boolean registered; // True if the test service instance is currently registered.
14035 Boolean testFailed; // True if at least one test case failed.
14036
14037 } ProbeConflictTestContext;
14038
14039 static void DNSSD_API
14040 _ProbeConflictTestRegisterCallback(
14041 DNSServiceRef inSDRef,
14042 DNSServiceFlags inFlags,
14043 DNSServiceErrorType inError,
14044 const char * inName,
14045 const char * inType,
14046 const char * inDomain,
14047 void * inContext );
14048 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
14049 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
14050 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
14051 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
14052
14053 static void ProbeConflictTestCmd( void )
14054 {
14055 OSStatus err;
14056 ProbeConflictTestContext * context;
14057 const char * serviceName;
14058 char tag[ 6 + 1 ];
14059
14060 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
14061 require_action( context, exit, err = kNoMemoryErr );
14062
14063 if( gProbeConflictTest_Interface )
14064 {
14065 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
14066 require_noerr_quiet( err, exit );
14067 }
14068 else
14069 {
14070 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14071 require_noerr_quiet( err, exit );
14072 }
14073
14074 if( gProbeConflictTest_OutputFilePath )
14075 {
14076 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
14077 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
14078 }
14079
14080 err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
14081 require_noerr_quiet( err, exit );
14082
14083 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
14084 require_action( context->results, exit, err = kNoMemoryErr );
14085
14086 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
14087
14088 ASPrintF( &context->serviceType, "_pctest-%s._udp",
14089 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14090 require_action( context->serviceType, exit, err = kNoMemoryErr );
14091
14092 context->testStartTime = NanoTimeGetCurrent();
14093 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
14094 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
14095 require_noerr( err, exit );
14096
14097 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
14098 require_noerr( err, exit );
14099
14100 dispatch_main();
14101
14102 exit:
14103 exit( 1 );
14104 }
14105
14106 //===========================================================================================================================
14107 // _ProbeConflictTestRegisterCallback
14108 //===========================================================================================================================
14109
14110 static void DNSSD_API
14111 _ProbeConflictTestRegisterCallback(
14112 DNSServiceRef inSDRef,
14113 DNSServiceFlags inFlags,
14114 DNSServiceErrorType inError,
14115 const char * inName,
14116 const char * inType,
14117 const char * inDomain,
14118 void * inContext )
14119 {
14120 OSStatus err;
14121 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14122
14123 Unused( inSDRef );
14124 Unused( inType );
14125 Unused( inDomain );
14126
14127 err = inError;
14128 require_noerr( err, exit );
14129
14130 if( !context->registered )
14131 {
14132 if( inFlags & kDNSServiceFlagsAdd )
14133 {
14134 uint8_t * ptr;
14135 size_t recordNameLen;
14136 unsigned int len;
14137 uint8_t name[ kDomainNameLengthMax ];
14138
14139 context->registered = true;
14140
14141 FreeNullSafe( context->serviceName );
14142 context->serviceName = strdup( inName );
14143 require_action( context->serviceName, exit, err = kNoMemoryErr );
14144
14145 err = DomainNameFromString( name, context->serviceName, NULL );
14146 require_noerr( err, exit );
14147
14148 err = DomainNameAppendString( name, context->serviceType, NULL );
14149 require_noerr( err, exit );
14150
14151 err = DomainNameAppendString( name, "local", NULL );
14152 require_noerr( err, exit );
14153
14154 ForgetMem( &context->recordName );
14155 err = DomainNameDup( name, &context->recordName, &recordNameLen );
14156 require_noerr( err, exit );
14157 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14158
14159 // Make the first label all caps so that it's easier to spot in system logs.
14160
14161 ptr = context->recordName;
14162 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
14163
14164 err = _ProbeConflictTestStartNextTest( context );
14165 require_noerr( err, exit );
14166 }
14167 }
14168 else
14169 {
14170 if( !( inFlags & kDNSServiceFlagsAdd ) )
14171 {
14172 context->registered = false;
14173 err = _ProbeConflictTestStopCurrentTest( context, true );
14174 require_noerr( err, exit );
14175 }
14176 }
14177 err = kNoErr;
14178
14179 exit:
14180 if( err ) exit( 1 );
14181 }
14182
14183 //===========================================================================================================================
14184 // _ProbeConflictTestColliderStopHandler
14185 //===========================================================================================================================
14186
14187 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
14188 {
14189 OSStatus err;
14190 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14191
14192 err = inError;
14193 require_noerr_quiet( err, exit );
14194
14195 ForgetCF( &context->collider );
14196
14197 err = _ProbeConflictTestStopCurrentTest( context, false );
14198 require_noerr( err, exit );
14199
14200 err = _ProbeConflictTestStartNextTest( context );
14201 require_noerr( err, exit );
14202
14203 exit:
14204 if( err ) exit( 1 );
14205 }
14206
14207 //===========================================================================================================================
14208 // _ProbeConflictTestStartNextTest
14209 //===========================================================================================================================
14210
14211 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
14212 {
14213 OSStatus err;
14214 const ProbeConflictTestCase * testCase;
14215
14216 check( !inContext->collider );
14217
14218 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
14219 {
14220 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14221 }
14222 else
14223 {
14224 _ProbeConflictTestFinalizeAndExit( inContext );
14225 }
14226
14227 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
14228 require_noerr( err, exit );
14229
14230 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
14231 require_noerr( err, exit );
14232
14233 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
14234 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
14235 require_noerr( err, exit );
14236
14237 MDNSColliderSetProtocols( inContext->collider, kMDNSColliderProtocol_IPv4 );
14238 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
14239 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
14240
14241 inContext->startTime = NanoTimeGetCurrent();
14242 err = MDNSColliderStart( inContext->collider );
14243 require_noerr( err, exit );
14244
14245 exit:
14246 return( err );
14247 }
14248
14249 //===========================================================================================================================
14250 // _ProbeConflictTestStopCurrentTest
14251 //===========================================================================================================================
14252
14253 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14254 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14255 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14256 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14257 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14258 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14259
14260 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
14261 {
14262 OSStatus err;
14263 const ProbeConflictTestCase * testCase;
14264 NanoTime64 now;
14265 Boolean passed;
14266 char startTime[ 32 ];
14267 char endTime[ 32 ];
14268
14269 now = NanoTimeGetCurrent();
14270
14271 if( inContext->collider )
14272 {
14273 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
14274 MDNSColliderStop( inContext->collider );
14275 CFRelease( inContext->collider );
14276 inContext->collider = NULL;
14277 }
14278
14279 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14280 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
14281 if( !passed ) inContext->testFailed = true;
14282
14283 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
14284 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14285
14286 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
14287 "{"
14288 "%kO=%s" // description
14289 "%kO=%b" // expectedRename
14290 "%kO=%s" // startTime
14291 "%kO=%s" // endTime
14292 "%kO=%s" // serviceName
14293 "%kO=%b" // passed
14294 "}",
14295 kProbeConflictTestCaseResultKey_Description, testCase->description,
14296 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
14297 kProbeConflictTestCaseResultKey_StartTime, startTime,
14298 kProbeConflictTestCaseResultKey_EndTime, endTime,
14299 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
14300 kProbeConflictTestCaseResultKey_Passed, passed );
14301 require_noerr( err, exit );
14302
14303 ++inContext->testCaseIndex;
14304
14305 exit:
14306 return( err );
14307 }
14308
14309 //===========================================================================================================================
14310 // _ProbeConflictTestFinalizeAndExit
14311 //===========================================================================================================================
14312
14313 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14314 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14315 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14316 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14317 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14318
14319 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
14320 {
14321 OSStatus err;
14322 CFPropertyListRef plist;
14323 NanoTime64 now;
14324 char startTime[ 32 ];
14325 char endTime[ 32 ];
14326
14327 now = NanoTimeGetCurrent();
14328
14329 check( !inContext->collider );
14330
14331 _NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
14332 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14333
14334 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
14335 "{"
14336 "%kO=%s" // startTime
14337 "%kO=%s" // endTime
14338 "%kO=%s" // serviceType
14339 "%kO=%O" // results
14340 "%kO=%b" // passed
14341 "}",
14342 kProbeConflictTestReportKey_StartTime, startTime,
14343 kProbeConflictTestReportKey_EndTime, endTime,
14344 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
14345 kProbeConflictTestReportKey_Results, inContext->results,
14346 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
14347 require_noerr( err, exit );
14348 ForgetCF( &inContext->results );
14349
14350 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
14351 CFRelease( plist );
14352 require_noerr( err, exit );
14353
14354 exit( inContext->testFailed ? 2 : 0 );
14355
14356 exit:
14357 ErrQuit( 1, "error: %#m\n", err );
14358 }
14359
14360 //===========================================================================================================================
14361 // ExpensiveConstrainedsTestCmd
14362 //===========================================================================================================================
14363
14364 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14365 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14366 #define LOOPBACK_INTERFACE_NAME "lo0"
14367 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14368 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14369 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14370 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14371 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14372
14373 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14374 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14375 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14376 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14377
14378 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14379 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14380 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14381 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14382 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14383 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14384 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14385 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14386 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14387 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14388
14389 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14390 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14391 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14392 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14393 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14396 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14397
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14402 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14403
14404 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14405 enum ExpensiveConstrainedTestState
14406 {
14407 TEST_BEGIN,
14408 TEST_EXPENSIVE_PREPARE,
14409 TEST_EXPENSIVE, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14410 TEST_CONSTRAINED_PREPARE,
14411 TEST_CONSTRAINED, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14412 TEST_EXPENSIVE_CONSTRAINED_PREPARE,
14413 TEST_EXPENSIVE_CONSTRAINED, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14414 TEST_FAILED,
14415 TEST_SUCCEEDED
14416 };
14417 enum ExpensiveConstrainedTestOperation
14418 {
14419 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.
14420 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.
14421 NO_UPDATE // no status update notification
14422 };
14423
14424 typedef struct
14425 {
14426 uint32_t subtestIndex; // The index of parameter for the subtest
14427 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
14428 const char * name; // Hostname to resolve.
14429 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
14430 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
14431 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
14432 char ifName[IFNAMSIZ]; // Interface name for the given interface index.
14433 dispatch_source_t timer; // The test will check if the current behavior is valid, which is called by
14434 // the timer per 2s.
14435 pid_t serverPID;
14436 Boolean isExpensivePrev; // If the interface is expensive in the previous test step.
14437 Boolean isExpensiveNow; // If the interface is expensive now.
14438 Boolean isConstrainedPrev; // If the interface is constrained in the previous test step.
14439 Boolean isConstrainedNow; // If the interface is constrained now.
14440 Boolean startFromExpensive; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14441 uint8_t numOfRetries; // the number of retries we can have if the test fail
14442 struct timeval updateTime; // The time when interface status(expensive or constrained) is changed.
14443 struct timeval notificationTime; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14444 uint32_t counter; // To record how many times the test has repeated.
14445 enum ExpensiveConstrainedTestState state; // The current test state.
14446 enum ExpensiveConstrainedTestOperation expectedOperation; // the test expects this kind of notification
14447 enum ExpensiveConstrainedTestOperation operation; // represents what notification the callback function gets.
14448
14449 NanoTime64 testReport_startTime; // when the entire test starts
14450 CFMutableArrayRef subtestReport; // stores the log message for every subtest
14451 NanoTime64 subtestReport_startTime; // when the subtest starts
14452 CFMutableArrayRef subtestProgress; // one test iteration
14453 NanoTime64 subtestProgress_startTime; // when the test iteration starts
14454 CFMutableArrayRef subtestProgress_callBack; // array of ADD/REMOVE events
14455 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14456 OutputFormatType outputFormat; // Format of test report output.
14457 } ExpensiveConstrainedContext;
14458
14459 // structure that controls how the subtest is run
14460 typedef struct
14461 {
14462 const char *qname; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14463 Boolean deny_expensive; // if the query should avoid using expensive interface
14464 Boolean deny_constrained; // if the query should avoid using constrained interface
14465 Boolean start_from_expensive; // if the query should starts from using an expensive interface
14466 Boolean ipv4_query; // only allow IPv4 query
14467 Boolean ipv6_query; // only allow IPv6 query
14468 int8_t test_passed; // if the subtest passes
14469 } ExpensiveConstrainedTestParams;
14470
14471 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams[] =
14472 {
14473 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14474 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14475 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14476 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14477 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14478 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14479 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14480 // IPv4 Only
14481 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14482 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14483 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14484 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14485 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14486 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14487 // IPv6 Only
14488 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14489 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14493 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14494 };
14495
14496 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
14497 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
14498 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
14499 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
14500 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
14501 static void DNSSD_API
14502 ExpensiveConstrainedCallback(
14503 DNSServiceRef inSDRef,
14504 DNSServiceFlags inFlags,
14505 uint32_t inInterfaceIndex,
14506 DNSServiceErrorType inError,
14507 const char * inHostname,
14508 const struct sockaddr * inSockAddr,
14509 uint32_t inTTL,
14510 void * inContext );
14511 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
14512 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
14513 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
14514 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
14515 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
14516 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
14517 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
14518 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
14519 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
14520
14521 //===========================================================================================================================
14522 // ExpensiveConstrainedTestCmd
14523 //===========================================================================================================================
14524
14525 static void ExpensiveConstrainedTestCmd( void )
14526 {
14527 OSStatus err;
14528 dispatch_source_t signalSource = NULL;
14529 ExpensiveConstrainedContext * context = NULL;
14530
14531 // Set up SIGINT handler.
14532 signal( SIGINT, SIG_IGN );
14533 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
14534 require_noerr( err, exit );
14535 dispatch_resume( signalSource );
14536
14537 // create the test context
14538 context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
14539 require_action( context, exit, err = kNoMemoryErr );
14540
14541 // get the command line option
14542 err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
14543 require_noerr_quiet( err, exit );
14544 if ( gExpensiveConstrainedTest_OutputFilePath )
14545 {
14546 context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
14547 require_noerr_quiet( context->outputFilePath, exit );
14548 }
14549
14550 // initialize context
14551 context->subtestIndex = 0;
14552 context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
14553
14554 // initialize the CFArray used to store the log
14555 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14556 context->testReport_startTime = NanoTimeGetCurrent();
14557
14558 // setup local DNS server
14559 ExpensiveConstrainedSetupLocalDNSServer( context );
14560
14561 ExpensiveConstrainedStartTestHandler( context );
14562
14563 dispatch_main();
14564
14565 exit:
14566 exit( 1 );
14567 }
14568
14569 //===========================================================================================================================
14570 // ExpensiveConstrainedSetupLocalDNSServer
14571 //===========================================================================================================================
14572
14573 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
14574 {
14575 pid_t current_pid = getpid();
14576 OSStatus err = SpawnCommand( &context->serverPID, "dnssdutil server -l --follow %d", current_pid );
14577 if (err != 0)
14578 {
14579 FPrintF( stdout, "dnssdutil server -l --follow <PID> failed, error: %d\n", err );
14580 exit( 1 );
14581 }
14582 sleep(2);
14583 }
14584
14585 //===========================================================================================================================
14586 // ExpensiveConstrainedStartTestHandler
14587 //===========================================================================================================================
14588
14589 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
14590 {
14591 // setup 3s timer
14592 ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
14593
14594 // set the event handler for the 3s timer
14595 dispatch_source_set_event_handler( context->timer, ^{
14596 ExpensiveConstrainedTestTimerEventHandler( context );
14597 } );
14598
14599 dispatch_resume( context->timer );
14600 }
14601
14602 //===========================================================================================================================
14603 // ExpensiveConstrainedStartTestHandler
14604 //===========================================================================================================================
14605
14606 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
14607 {
14608 dispatch_cancel( context->timer );
14609 dispatch_release( context->timer );
14610 context->timer = NULL;
14611 }
14612
14613 //===========================================================================================================================
14614 // ExpensiveConstrainedSetupTimer
14615 //===========================================================================================================================
14616
14617 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
14618 {
14619 // set the timer source, the event handler will be called for every "second" seconds
14620 context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
14621 if ( context->timer == NULL )
14622 {
14623 FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14624 exit( 1 );
14625 }
14626 // the first block will be put into the queue "second"s after calling dispatch_resume
14627 dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
14628 (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
14629 }
14630
14631 //===========================================================================================================================
14632 // ExpensiveConstrainedTestTimerEventHandler
14633 //===========================================================================================================================
14634
14635 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
14636 {
14637 OSStatus err;
14638 char buffer[ 1024 ];
14639 const char *errorDescription = NULL;
14640
14641 // do not log the state if we are in transition state
14642 if (context->state != TEST_BEGIN
14643 && context->state != TEST_SUCCEEDED
14644 && context->state != TEST_CONSTRAINED_PREPARE
14645 && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
14646 ExpensiveConstrainedSubtestProgressReport( context );
14647
14648 switch ( context->state ) {
14649 case TEST_BEGIN:
14650 {
14651 ExpensiveConstrainedStopTestHandler( context );
14652
14653 // clear mDNSResponder cache
14654 err = systemf( NULL, "killall -HUP mDNSResponder" );
14655 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14656
14657 // initialize the global parameters
14658 ExpensiveConstrainedInitializeContext( context );
14659
14660 // The local DNS server is set up on the local only interface.
14661 gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
14662 strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
14663
14664 // The local DNS server is unscoped, so we must set our question to unscoped.
14665 context->ifIndex = kDNSServiceInterfaceIndexAny;
14666
14667 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14668 // use the local dns server set up previously to run the test locally.
14669 require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
14670 SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
14671 errorDescription = buffer );
14672
14673 // get the quesion name
14674 context->name = gExpensiveConstrainedTest_Name;
14675
14676 // set the initial state for the interface
14677 context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
14678 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
14679 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14680 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14681
14682 // get question flag
14683 if ( gExpensiveConstrainedTest_DenyExpensive ) context->flags |= kDNSServiceFlagsDenyExpensive;
14684 if ( gExpensiveConstrainedTest_DenyConstrained ) context->flags |= kDNSServiceFlagsDenyConstrained;
14685 if ( gExpensiveConstrainedTest_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
14686 if ( gExpensiveConstrainedTest_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
14687
14688 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14689 #if( TARGET_OS_WATCH )
14690 context->flags |= kDNSServiceFlagsPathEvaluationDone;
14691 #endif
14692
14693 // start the query
14694 DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
14695
14696 // set the initial test status
14697 context->subtestReport_startTime = NanoTimeGetCurrent();
14698 context->subtestProgress_startTime = NanoTimeGetCurrent();
14699 context->state = TEST_EXPENSIVE_PREPARE; // start from expensive test
14700 context->isExpensiveNow = context->startFromExpensive ? true : false;
14701 context->isConstrainedNow = false;
14702 context->expectedOperation = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
14703 context->operation = NO_UPDATE;
14704 context->subtestProgress = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14705 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14706 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14707 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14708
14709 // set the queue where the callback will be called when there is an answer for the query
14710 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
14711 require_noerr( err, test_failed );
14712
14713 ExpensiveConstrainedStartTestHandler( context );
14714 }
14715 break;
14716 case TEST_EXPENSIVE_PREPARE:
14717 require_action( context->isConstrainedNow == false, test_failed,
14718 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14719 errorDescription = buffer );
14720 require_action( context->expectedOperation == context->operation, test_failed,
14721 errorDescription = "Operation is not expected" );
14722
14723 context->subtestProgress_startTime = NanoTimeGetCurrent();
14724 context->state = TEST_EXPENSIVE; // begin to test expensive flag
14725 context->counter = 0; // the number of test repetition that has passed
14726 context->isExpensivePrev = context->isExpensiveNow;
14727 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14728 context->isConstrainedPrev = false; // the interface is currently unconstrained
14729 context->isConstrainedNow = false; // the interface will be unconstrained in the current test
14730 if ( gExpensiveConstrainedTest_DenyExpensive )
14731 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14732 else
14733 context->expectedOperation = NO_UPDATE;
14734 context->operation = NO_UPDATE; // NO_UPDATE means the call back function has not been called
14735 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14736 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14737
14738 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14739 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14740
14741 // record the starting timestamp
14742 gettimeofday( &context->updateTime, NULL );
14743
14744 break;
14745 case TEST_EXPENSIVE:
14746 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14747 require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
14748 SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
14749 errorDescription = buffer );
14750 // constrained flag is always turned off when testing expensive
14751 require_action( context->isConstrainedNow == false, test_failed,
14752 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
14753 errorDescription = buffer );
14754 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14755
14756 context->counter++; // one test repetition has passed
14757 if ( context->counter == TEST_REPETITION ) // expensive test finished
14758 {
14759 // prepare to test constrained flag
14760 context->state = TEST_CONSTRAINED_PREPARE;
14761
14762 // reset the interface
14763 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14764 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14765
14766 context->isExpensiveNow = false;
14767 context->isConstrainedNow = false;
14768 gettimeofday( &context->updateTime, NULL );
14769 }
14770 else
14771 {
14772 context->subtestProgress_startTime = NanoTimeGetCurrent();
14773 context->isExpensivePrev = context->isExpensiveNow;
14774 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14775 if ( gExpensiveConstrainedTest_DenyExpensive )
14776 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14777 else
14778 context->expectedOperation = NO_UPDATE;
14779 context->operation = NO_UPDATE;
14780 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14781 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14782
14783 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14784 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14785
14786 gettimeofday( &context->updateTime, NULL );
14787 }
14788 break;
14789 case TEST_CONSTRAINED_PREPARE:
14790 // The interface should be inexpensive and unconstrained when the constrained test starts
14791 require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
14792 errorDescription = buffer );
14793 require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14794 errorDescription = buffer );
14795
14796 context->subtestProgress_startTime = NanoTimeGetCurrent();
14797 context->state = TEST_CONSTRAINED; // constrained interface is now under testing
14798 context->counter = 0;
14799 context->isExpensivePrev = false;
14800 context->isExpensiveNow = false;
14801 context->isConstrainedPrev = false;
14802 context->isConstrainedNow = true; // will set constrained flag on the interface
14803 if ( gExpensiveConstrainedTest_DenyConstrained )
14804 context->expectedOperation = RESULT_RMV;
14805 else
14806 context->expectedOperation = NO_UPDATE;
14807 context->operation = NO_UPDATE;
14808 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14809 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14810
14811 // change interface to the constrained one
14812 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
14813 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14814
14815 gettimeofday( &context->updateTime, NULL );
14816 break;
14817 case TEST_CONSTRAINED:
14818 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14819 require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
14820 SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
14821 errorDescription = buffer );
14822 require_action( context->isExpensiveNow == false, test_failed,
14823 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
14824 errorDescription = buffer );
14825 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
14826
14827 context->counter++;
14828 if (context->counter == TEST_REPETITION)
14829 {
14830 // test changing expensive and constrained flags at the same time
14831 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
14832
14833 // reset interface
14834 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14835 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14836
14837 context->isExpensiveNow = false;
14838 context->isConstrainedNow = false;
14839 gettimeofday( &context->updateTime, NULL );
14840 }
14841 else
14842 {
14843 context->subtestProgress_startTime = NanoTimeGetCurrent();
14844 context->isConstrainedPrev = context->isConstrainedNow;
14845 context->isConstrainedNow = !context->isConstrainedNow; // flip constrained flag
14846 if ( gExpensiveConstrainedTest_DenyConstrained )
14847 context->expectedOperation = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
14848 else
14849 context->expectedOperation = NO_UPDATE;
14850 context->operation = NO_UPDATE;
14851 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14852 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14853
14854 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
14855 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14856
14857 gettimeofday(&context->updateTime, NULL);
14858 }
14859 break;
14860 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
14861 // The interface should be inexpensive and unconstrained when the constrained test starts
14862 require_action( context->isExpensiveNow == false, test_failed,
14863 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
14864 errorDescription = buffer );
14865 require_action( context->isConstrainedNow == false, test_failed,
14866 SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14867 errorDescription = buffer );
14868
14869 // now flip expensive and constrained at the same time
14870 context->subtestProgress_startTime = NanoTimeGetCurrent();
14871 context->state = TEST_EXPENSIVE_CONSTRAINED;
14872 context->counter = 0;
14873 context->isExpensivePrev = false;
14874 context->isExpensiveNow = true;
14875 context->isConstrainedPrev = false;
14876 context->isConstrainedNow = true;
14877 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14878 context->expectedOperation = RESULT_RMV;
14879 else
14880 context->expectedOperation = NO_UPDATE;
14881 context->operation = NO_UPDATE;
14882 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14883 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14884
14885 err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
14886 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14887
14888 gettimeofday( &context->updateTime, NULL );
14889 break;
14890 case TEST_EXPENSIVE_CONSTRAINED:
14891 // expensive and constrained flag should always be changed
14892 require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
14893 SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
14894 errorDescription = buffer );
14895 require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
14896 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14897
14898 context->counter++;
14899 if ( context->counter == TEST_REPETITION )
14900 {
14901 context->state = TEST_SUCCEEDED;
14902 }
14903 else
14904 {
14905 context->subtestProgress_startTime = NanoTimeGetCurrent();
14906 context->isExpensivePrev = context->isExpensiveNow;
14907 context->isExpensiveNow = !context->isExpensiveNow;
14908 context->isConstrainedPrev = context->isConstrainedNow;
14909 context->isConstrainedNow = !context->isConstrainedNow;
14910 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14911 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14912 else
14913 context->expectedOperation = NO_UPDATE;
14914 context->operation = NO_UPDATE;
14915 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14916 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14917
14918 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
14919 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14920
14921 gettimeofday( &context->updateTime, NULL );
14922 }
14923 break;
14924 case TEST_FAILED:
14925 test_failed:
14926 ExpensiveConstrainedSubtestReport( context, errorDescription );
14927 ExpensiveConstrainedStopAndCleanTheTest( context );
14928 if ( context->numOfRetries > 0 )
14929 {
14930 context->state = TEST_BEGIN;
14931 context->numOfRetries--;
14932 break;
14933 }
14934 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
14935 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14936 {
14937 ExpensiveConstrainedFinalResultReport( context, false );
14938 exit( 2 );
14939 }
14940 if (context->timer == NULL)
14941 {
14942 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14943 ExpensiveConstrainedFinalResultReport( context, false );
14944 exit( 1 );
14945 }
14946 context->state = TEST_BEGIN;
14947 break;
14948 case TEST_SUCCEEDED:
14949 ExpensiveConstrainedSubtestReport( context, NULL );
14950 ExpensiveConstrainedStopAndCleanTheTest( context );
14951 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
14952 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14953 {
14954 // all the subtests have been run
14955 Boolean hasFailed = false;
14956 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
14957 hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
14958
14959 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
14960 exit( hasFailed ? 2 : 0 );
14961 }
14962 context->state = TEST_BEGIN;
14963 break;
14964 default:
14965 FPrintF( stdout, "unknown error\n" );
14966 exit( 1 );
14967 }
14968 }
14969
14970 //===========================================================================================================================
14971 // ExpensiveConstrainedCallback
14972 //===========================================================================================================================
14973
14974 static void DNSSD_API
14975 ExpensiveConstrainedCallback(
14976 __unused DNSServiceRef inSDRef,
14977 DNSServiceFlags inFlags,
14978 uint32_t inInterfaceIndex,
14979 DNSServiceErrorType inError,
14980 const char * inHostname,
14981 const struct sockaddr * inSockAddr,
14982 __unused uint32_t inTTL,
14983 void * inContext )
14984 {
14985 ExpensiveConstrainedContext * const context = (ExpensiveConstrainedContext *)inContext;
14986 OSStatus err;
14987 const char * addrStr;
14988 char addrStrBuf[ kSockAddrStringMaxSize ];
14989 char inFlagsDescription[ 128 ];
14990 NanoTime64 now;
14991 char nowTimestamp[ 32 ];
14992
14993 switch ( inError ) {
14994 case kDNSServiceErr_NoError:
14995 case kDNSServiceErr_NoSuchRecord:
14996 break;
14997
14998 case kDNSServiceErr_Timeout:
14999 Exit( kExitReason_Timeout );
15000
15001 default:
15002 err = inError;
15003 goto exit;
15004 }
15005
15006 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
15007 {
15008 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
15009 err = kTypeErr;
15010 goto exit;
15011 }
15012
15013 if( !inError )
15014 {
15015 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
15016 require_noerr( err, exit );
15017 addrStr = addrStrBuf;
15018 }
15019 else
15020 {
15021 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
15022 }
15023
15024 now = NanoTimeGetCurrent();
15025 _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
15026 SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
15027 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress_callBack,
15028 "{"
15029 "%kO=%s"
15030 "%kO=%s"
15031 "%kO=%s"
15032 "%kO=%lli"
15033 "%kO=%s"
15034 "}",
15035 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP, nowTimestamp,
15036 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME, inHostname,
15037 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS, inFlagsDescription,
15038 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE, (int64_t) inInterfaceIndex,
15039 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS, addrStr
15040 );
15041 require_noerr_quiet( err, exit );
15042
15043 if ( inFlags & kDNSServiceFlagsMoreComing )
15044 return;
15045
15046 if ( inFlags & kDNSServiceFlagsAdd )
15047 context->operation = RESULT_ADD;
15048 else
15049 context->operation = RESULT_RMV;
15050
15051 gettimeofday(&context->notificationTime, NULL);
15052 exit:
15053 if( err ) exit( 1 );
15054 }
15055
15056 //===========================================================================================================================
15057 // ExpensiveConstrainedInitializeContext
15058 //===========================================================================================================================
15059
15060 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
15061 {
15062 // clear the flags of the previous subtest
15063 context->flags = 0;
15064 context->protocols = 0;
15065
15066 // get the parameter for the current subtest
15067 const ExpensiveConstrainedTestParams *param = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
15068 gExpensiveConstrainedTest_Name = param->qname;
15069 gExpensiveConstrainedTest_DenyExpensive = param->deny_expensive;
15070 gExpensiveConstrainedTest_DenyConstrained = param->deny_constrained;
15071 gExpensiveConstrainedTest_StartFromExpensive = param->start_from_expensive;
15072 gExpensiveConstrainedTest_ProtocolIPv4 = param->ipv4_query;
15073 gExpensiveConstrainedTest_ProtocolIPv6 = param->ipv6_query;
15074 }
15075
15076 //===========================================================================================================================
15077 // ExpensiveConstrainedStopAndCleanTheTest
15078 //===========================================================================================================================
15079
15080 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
15081 {
15082 // Stop the ongoing query
15083 if ( context->opRef != NULL )
15084 DNSServiceRefDeallocate( context->opRef );
15085
15086 context->opRef = NULL;
15087 context->flags = 0;
15088 context->protocols = 0;
15089 }
15090
15091 //===========================================================================================================================
15092 // ExpensiveConstrainedSubtestProgressReport
15093 //===========================================================================================================================
15094
15095 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
15096 {
15097 OSStatus err;
15098 NanoTime64 now;
15099 char startTime[ 32 ];
15100 char endTime[ 32 ];
15101 char expensive[ 32 ];
15102 char constrained[ 32 ];
15103
15104 now = NanoTimeGetCurrent();
15105 _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
15106 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15107
15108 snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
15109 snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
15110
15111 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
15112 "{"
15113 "%kO=%s"
15114 "%kO=%s"
15115 "%kO=%s"
15116 "%kO=%s"
15117 "%kO=%s"
15118 "%kO=%s"
15119 "%kO=%s"
15120 "%kO=%O"
15121 "}",
15122 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME, startTime,
15123 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME, endTime,
15124 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE, ExpensiveConstrainedStateString(context->state),
15125 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT, ExpensiveConstrainedOperationString(context->expectedOperation),
15126 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT, ExpensiveConstrainedOperationString(context->operation),
15127 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW, expensive,
15128 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW, constrained,
15129 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK, context->subtestProgress_callBack
15130 );
15131 require_noerr( err, exit );
15132 ForgetCF( &context->subtestProgress_callBack );
15133 return;
15134
15135 exit:
15136 ErrQuit( 1, "error: %#m\n", err );
15137 }
15138
15139 //===========================================================================================================================
15140 // ExpensiveConstrainedFinalSubtestReport
15141 //===========================================================================================================================
15142
15143 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
15144 {
15145 OSStatus err;
15146 NanoTime64 now;
15147 char startTime[ 32 ];
15148 char endTime[ 32 ];
15149 char flagDescription[ 1024 ];
15150
15151 now = NanoTimeGetCurrent();
15152 _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
15153 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15154 SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
15155
15156 if (error_description != NULL)
15157 {
15158 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15159 "{"
15160 "%kO=%s"
15161 "%kO=%s"
15162 "%kO=%s"
15163 "%kO=%s"
15164 "%kO=%s"
15165 "%kO=%lli"
15166 "%kO=%s"
15167 "%kO=%O"
15168 "%kO=%s"
15169 "%kO=%O"
15170 "}",
15171 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15172 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15173 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15174 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15175 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15176 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15177 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15178 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Fail" ),
15179 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR, error_description,
15180 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15181 );
15182 }
15183 else
15184 {
15185 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15186 "{"
15187 "%kO=%s"
15188 "%kO=%s"
15189 "%kO=%s"
15190 "%kO=%s"
15191 "%kO=%s"
15192 "%kO=%lli"
15193 "%kO=%s"
15194 "%kO=%O"
15195 "%kO=%O"
15196 "}",
15197 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15198 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15199 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15200 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15201 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15202 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15203 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15204 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Pass" ),
15205 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15206 );
15207 }
15208
15209 require_noerr( err, exit );
15210 ForgetCF( &context->subtestProgress );
15211 return;
15212 exit:
15213 ErrQuit( 1, "error: %#m\n", err );
15214 }
15215
15216 //===========================================================================================================================
15217 // ExpensiveConstrainedFinalResultReport
15218 //===========================================================================================================================
15219
15220 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
15221 {
15222 OSStatus err;
15223 CFPropertyListRef plist;
15224 NanoTime64 now;
15225 char startTime[ 32 ];
15226 char endTime[ 32 ];
15227
15228 now = NanoTimeGetCurrent();
15229 _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
15230 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15231
15232 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15233 "{"
15234 "%kO=%s"
15235 "%kO=%s"
15236 "%kO=%b"
15237 "%kO=%O"
15238 "}",
15239 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME, startTime,
15240 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME, endTime,
15241 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED, allPassed,
15242 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT, context->subtestReport
15243 );
15244 require_noerr( err, exit );
15245 ForgetCF( &context->subtestReport );
15246
15247 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15248 CFRelease( plist );
15249 require_noerr( err, exit );
15250
15251 return;
15252 exit:
15253 ErrQuit( 1, "error: %#m\n", err );
15254 }
15255
15256 //===========================================================================================================================
15257 // ExpensiveConstrainedProtocolString
15258 //===========================================================================================================================
15259
15260 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
15261 {
15262 const char *str = NULL;
15263 switch ( protocol ) {
15264 case kDNSServiceProtocol_IPv4:
15265 str = "IPv4";
15266 break;
15267 case kDNSServiceProtocol_IPv6:
15268 str = "IPv6";
15269 break;
15270 case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
15271 str = "IPv4 & IPv6";
15272 break;
15273 default:
15274 break;
15275 }
15276 return str;
15277 }
15278
15279 //===========================================================================================================================
15280 // ExpensiveConstrainedStateString
15281 //===========================================================================================================================
15282
15283 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
15284 {
15285 const char *str = NULL;
15286 switch ( state ) {
15287 case TEST_BEGIN:
15288 str = "TEST_BEGIN";
15289 break;
15290 case TEST_EXPENSIVE_PREPARE:
15291 str = "TEST_EXPENSIVE_PREPARE";
15292 break;
15293 case TEST_EXPENSIVE:
15294 str = "TEST_EXPENSIVE";
15295 break;
15296 case TEST_CONSTRAINED_PREPARE:
15297 str = "TEST_CONSTRAINED_PREPARE";
15298 break;
15299 case TEST_CONSTRAINED:
15300 str = "TEST_CONSTRAINED";
15301 break;
15302 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
15303 str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15304 break;
15305 case TEST_EXPENSIVE_CONSTRAINED:
15306 str = "TEST_EXPENSIVE_CONSTRAINED";
15307 break;
15308 case TEST_FAILED:
15309 str = "TEST_FAILED";
15310 break;
15311 case TEST_SUCCEEDED:
15312 str = "TEST_SUCCEEDED";
15313 break;
15314 default:
15315 str = "UNKNOWN";
15316 break;
15317 }
15318
15319 return str;
15320 }
15321
15322 //===========================================================================================================================
15323 // ExpensiveConstrainedOperationString
15324 //===========================================================================================================================
15325
15326 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
15327 {
15328 const char *str = NULL;
15329 switch ( operation ) {
15330 case RESULT_ADD:
15331 str = "RESULT_ADD";
15332 break;
15333 case RESULT_RMV:
15334 str = "RESULT_RMV";
15335 break;
15336 case NO_UPDATE:
15337 str = "NO_UPDATE";
15338 break;
15339 default:
15340 str = "UNKNOWN";
15341 break;
15342 }
15343 return str;
15344 }
15345
15346 //===========================================================================================================================
15347 // expensiveConstrainedEndsWith
15348 //===========================================================================================================================
15349 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
15350 {
15351 if ( !str || !suffix )
15352 return false;
15353 size_t lenstr = strlen( str );
15354 size_t lensuffix = strlen( suffix );
15355 if ( lensuffix > lenstr )
15356 return false;
15357 return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
15358 }
15359
15360 //===========================================================================================================================
15361 // RegistrationTestCmd
15362 //===========================================================================================================================
15363
15364 typedef struct RegistrationSubtest RegistrationSubtest;
15365
15366 typedef struct
15367 {
15368 CFMutableArrayRef subtestReports; // Array of subtest reports.
15369 dispatch_source_t timer; // Timer to enforce subtest durations.
15370 dispatch_source_t sigSourceINT; // SIGINT signal handler for a clean test exit.
15371 dispatch_source_t sigSourceTERM; // SIGTERM signal handler for a clean test exit.
15372 RegistrationSubtest * subtest; // Current subtest.
15373 char * outputFilePath; // Path of test result output file. If NULL, stdout will be used.
15374 OutputFormatType outputFormat; // Format of test results output.
15375 CFStringRef computerNamePrev; // Previous ComputerName.
15376 CFStringRef localHostNamePrev; // Previous LocalHostName.
15377 NanoTime64 startTime; // Test's start time.
15378 char * computerName; // Temporary ComputerName to set during testing. (malloc'd)
15379 char * localHostName; // Temporary LocalHostName to set during testing. (malloc'd)
15380 CFStringEncoding computerNamePrevEncoding; // Previous ComputerName's encoding.
15381 int subtestIndex; // Index of current subtest.
15382 Boolean computerNameSet; // True if a temporary ComputerName was set.
15383 Boolean localHostNameSet; // True if a temporary LocalHostName was set.
15384 Boolean failed; // True if at least one non-skipped subtest failed.
15385 Boolean forBATS; // True if the test is running in a BATS environment.
15386
15387 } RegistrationTest;
15388
15389 typedef enum
15390 {
15391 kRegistrationInterfaceSet_Null = 0,
15392 kRegistrationInterfaceSet_All = 1,
15393 kRegistrationInterfaceSet_AllPlusAWDL = 2,
15394 kRegistrationInterfaceSet_LoopbackOnly = 3,
15395 kRegistrationInterfaceSet_AWDLOnly = 4
15396
15397 } RegistrationInterfaceSet;
15398
15399 typedef struct
15400 {
15401 RegistrationInterfaceSet interfaceSet; // Interfaces to register the service over.
15402 Boolean useDefaultName; // True if registration is to use the default service name.
15403 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15404
15405 } RegistrationSubtestParams;
15406
15407 static const RegistrationSubtestParams kRegistrationSubtestParams[] =
15408 {
15409 { kRegistrationInterfaceSet_All, true, false },
15410 { kRegistrationInterfaceSet_All, false, false },
15411 { kRegistrationInterfaceSet_AllPlusAWDL, true, false },
15412 { kRegistrationInterfaceSet_AllPlusAWDL, false, false },
15413 { kRegistrationInterfaceSet_LoopbackOnly, true, false },
15414 { kRegistrationInterfaceSet_LoopbackOnly, false, false },
15415 { kRegistrationInterfaceSet_AWDLOnly, true, false },
15416 { kRegistrationInterfaceSet_AWDLOnly, false, false },
15417 { kRegistrationInterfaceSet_All, true, true },
15418 { kRegistrationInterfaceSet_All, false, true },
15419 { kRegistrationInterfaceSet_AllPlusAWDL, true, true },
15420 { kRegistrationInterfaceSet_AllPlusAWDL, false, true },
15421 { kRegistrationInterfaceSet_LoopbackOnly, true, true },
15422 { kRegistrationInterfaceSet_LoopbackOnly, false, true },
15423 { kRegistrationInterfaceSet_AWDLOnly, true, true },
15424 { kRegistrationInterfaceSet_AWDLOnly, false, true }
15425 };
15426
15427 typedef struct
15428 {
15429 NanoTime64 browseResultTime; // Per-interface browse result time.
15430 NanoTime64 querySRVResultTime; // Per-interface SRV record query result time.
15431 NanoTime64 queryTXTResultTime; // Per-interface TXT record query result time.
15432
15433 } RegistrationResultTimes;
15434
15435 typedef struct
15436 {
15437 MDNSInterfaceItem base; // Underlying MDNSInterface linked-list item.
15438 RegistrationResultTimes times; // Per-interface result times.
15439
15440 } RegistrationInterfaceItem;
15441
15442 struct RegistrationSubtest
15443 {
15444 DNSServiceRef registration; // DNS-SD service registration.
15445 DNSServiceRef connection; // Shared DNS-SD connection.
15446 DNSServiceRef browse; // DNS-SD browse for service's type.
15447 DNSServiceRef querySRV; // DNS-SD query request for service's SRV record.
15448 DNSServiceRef queryTXT; // DNS-SD query request for service's TXT record.
15449 CFMutableArrayRef unexpected; // Array of unexpected registration, browse, and query results.
15450 #if( TARGET_OS_WATCH )
15451 CFMutableArrayRef ignored; // Array of unexpected, but ignored, browse and query results.
15452 #endif
15453 const char * serviceName; // Service's name.
15454 char * serviceNameCustom; // Service's name if using a custom name. (malloc'd)
15455 char * serviceType; // Service's service type. (malloc'd)
15456 size_t serviceTypeLen; // C string length of service's service type.
15457 char * serviceFQDN; // Service's FQDN, i.e., name of its SRV and TXT records.
15458 uint8_t * txtPtr; // Pointer to service's TXT record data. (malloc'd)
15459 size_t txtLen; // Length of service's TXT record data.
15460 RegistrationInterfaceItem * ifList; // If ifIndex == 0, interfaces that service should register over.
15461 RegistrationResultTimes ifTimes; // If ifIndex != 0, result times for interface with that index.
15462 RegistrationTest * test; // Pointer to parent test.
15463 NanoTime64 startTime; // Subtest's start time.
15464 char * description; // Subtest's description. (malloc'd)
15465 uint32_t ifIndex; // Interface index used for service registration.
15466 uint16_t port; // Service's port number.
15467 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15468 Boolean includeAWDL; // True if the IncludeAWDL flag was used during registration.
15469 Boolean ifIsAWDL; // True if ifIndex is the index of an AWDL interface.
15470 Boolean skipped; // True if this subtest is to be skipped.
15471 Boolean registered; // True if the test service was successfully registered.
15472 Boolean useDefaultName; // True if the service is to use the default service name.
15473 };
15474
15475 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest );
15476 static void _RegistrationTestFree( RegistrationTest *inTest );
15477 static void _RegistrationTestBegin( void *inContext );
15478 static void _RegistrationTestProceed( RegistrationTest *inTest );
15479 static OSStatus _RegistrationTestStart( RegistrationTest *inTest );
15480 static void _RegistrationTestStop( RegistrationTest *inTest );
15481 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15482 static OSStatus
15483 _RegistrationTestStartSubtest(
15484 RegistrationTest * inTest,
15485 const RegistrationSubtestParams * inParams,
15486 Boolean * outSkipped );
15487 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest );
15488 static void _RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
15489 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
15490 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
15491 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest );
15492 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest );
15493 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15494 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
15495 static OSStatus
15496 _RegistrationTestCreateRandomTXTRecord(
15497 size_t inMinLen,
15498 size_t inMaxLen,
15499 uint8_t ** outTXTPtr,
15500 size_t * outTXTLen );
15501 static void DNSSD_API
15502 _RegistrationSubtestRegisterCallback(
15503 DNSServiceRef inSDRef,
15504 DNSServiceFlags inFlags,
15505 DNSServiceErrorType inError,
15506 const char * inName,
15507 const char * inType,
15508 const char * inDomain,
15509 void * inContext );
15510 static void DNSSD_API
15511 _RegistrationSubtestBrowseCallback(
15512 DNSServiceRef inSDRef,
15513 DNSServiceFlags inFlags,
15514 uint32_t inIfIndex,
15515 DNSServiceErrorType inError,
15516 const char * inServiceName,
15517 const char * inServiceType,
15518 const char * inDomain,
15519 void * inContext );
15520 static void DNSSD_API
15521 _RegistrationSubtestQueryCallback(
15522 DNSServiceRef inSDRef,
15523 DNSServiceFlags inFlags,
15524 uint32_t inIfIndex,
15525 DNSServiceErrorType inError,
15526 const char * inName,
15527 uint16_t inType,
15528 uint16_t inClass,
15529 uint16_t inRDataLen,
15530 const void * inRDataPtr,
15531 uint32_t inTTL,
15532 void * inContext );
15533 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
15534 static RegistrationResultTimes *
15535 _RegistrationSubtestGetInterfaceResultTimes(
15536 RegistrationSubtest * inSubtest,
15537 uint32_t inIfIndex,
15538 Boolean * outIsAWDL );
15539 static void _RegistrationTestTimerHandler( void *inContext );
15540 #if( TARGET_OS_WATCH )
15541 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName );
15542 #endif
15543
15544 static void RegistrationTestCmd( void )
15545 {
15546 OSStatus err;
15547 RegistrationTest * test;
15548
15549 err = _RegistrationTestCreate( &test );
15550 require_noerr( err, exit );
15551
15552 if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
15553 if( gRegistrationTest_OutputFilePath )
15554 {
15555 test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
15556 require_action( test->outputFilePath, exit, err = kNoMemoryErr );
15557 }
15558
15559 err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
15560 require_noerr_quiet( err, exit );
15561
15562 dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
15563 dispatch_main();
15564
15565 exit:
15566 if( test ) _RegistrationTestFree( test );
15567 ErrQuit( 1, "error: %#m\n", err );
15568 }
15569
15570 //===========================================================================================================================
15571
15572 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest )
15573 {
15574 OSStatus err;
15575 RegistrationTest * obj;
15576
15577 obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
15578 require_action( obj, exit, err = kNoMemoryErr );
15579
15580 obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15581 require_action( obj->subtestReports, exit, err = kNoMemoryErr );
15582
15583 *outTest = obj;
15584 obj = NULL;
15585 err = kNoErr;
15586
15587 exit:
15588 if( obj ) _RegistrationTestFree( obj );
15589 return( err );
15590 }
15591
15592 //===========================================================================================================================
15593
15594 static void _RegistrationTestFree( RegistrationTest *inTest )
15595 {
15596 check( !inTest->timer );
15597 check( !inTest->sigSourceINT );
15598 check( !inTest->sigSourceTERM );
15599 check( !inTest->computerNameSet );
15600 check( !inTest->localHostNameSet );
15601 check( !inTest->subtest );
15602 ForgetCF( &inTest->subtestReports );
15603 ForgetMem( &inTest->outputFilePath );
15604 ForgetCF( &inTest->computerNamePrev );
15605 ForgetCF( &inTest->localHostNamePrev );
15606 ForgetMem( &inTest->computerName );
15607 ForgetMem( &inTest->localHostName );
15608 }
15609
15610 //===========================================================================================================================
15611
15612 static void _RegistrationTestBegin( void *inContext )
15613 {
15614 _RegistrationTestProceed( (RegistrationTest *) inContext );
15615 }
15616
15617 //===========================================================================================================================
15618
15619 static void _RegistrationTestProceed( RegistrationTest *inTest )
15620 {
15621 OSStatus err;
15622 Boolean skippedSubtest;
15623
15624 do
15625 {
15626 int subtestIndex;
15627
15628 if( !inTest->startTime )
15629 {
15630 err = _RegistrationTestStart( inTest );
15631 require_noerr_quiet( err, exit );
15632
15633 inTest->startTime = NanoTimeGetCurrent();
15634 }
15635 else
15636 {
15637 err = _RegistrationTestEndSubtest( inTest );
15638 require_noerr( err, exit );
15639
15640 ++inTest->subtestIndex;
15641 }
15642
15643 subtestIndex = inTest->subtestIndex;
15644 if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
15645 {
15646 err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
15647 require_noerr_quiet( err, exit );
15648 }
15649 else
15650 {
15651 _RegistrationTestEnd( inTest );
15652 }
15653
15654 } while( skippedSubtest );
15655
15656 exit:
15657 if( err ) _RegistrationTestExit( inTest, err );
15658 }
15659
15660 //===========================================================================================================================
15661
15662 static void _RegistrationTestSignalHandler( void *inContext );
15663
15664 static OSStatus _RegistrationTestStart( RegistrationTest *inTest )
15665 {
15666 OSStatus err;
15667 char tag[ 6 + 1 ];
15668
15669 // Save original ComputerName and LocalHostName.
15670
15671 check( !inTest->computerNamePrev );
15672 inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
15673 err = map_scerror( inTest->computerNamePrev );
15674 require_noerr( err, exit );
15675
15676 check( !inTest->localHostNamePrev );
15677 inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
15678 err = map_scerror( inTest->localHostNamePrev );
15679 require_noerr( err, exit );
15680
15681 // Generate a unique test ComputerName.
15682
15683 check( !inTest->computerName );
15684 ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
15685 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15686 require_action( inTest->computerName, exit, err = kNoMemoryErr );
15687
15688 // Generate a unique test LocalHostName.
15689
15690 check( !inTest->localHostName );
15691 ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
15692 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15693 require_action( inTest->localHostName, exit, err = kNoMemoryErr );
15694
15695 // Set up SIGINT signal handler.
15696
15697 signal( SIGINT, SIG_IGN );
15698 check( !inTest->sigSourceINT );
15699 err = DispatchSignalSourceCreate( SIGINT, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceINT );
15700 require_noerr( err, exit );
15701 dispatch_resume( inTest->sigSourceINT );
15702
15703 // Set up SIGTERM signal handler.
15704
15705 signal( SIGTERM, SIG_IGN );
15706 check( !inTest->sigSourceTERM );
15707 err = DispatchSignalSourceCreate( SIGTERM, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceTERM );
15708 require_noerr( err, exit );
15709 dispatch_resume( inTest->sigSourceTERM );
15710
15711 // Set test ComputerName.
15712
15713 check( !inTest->computerNameSet );
15714 err = _SetComputerNameWithUTF8CString( inTest->computerName );
15715 require_noerr( err, exit );
15716 inTest->computerNameSet = true;
15717
15718 // Set test LocalHostName.
15719
15720 check( !inTest->localHostNameSet );
15721 err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
15722 require_noerr( err, exit );
15723 inTest->localHostNameSet = true;
15724
15725 exit:
15726 if( err ) _RegistrationTestStop( inTest );
15727 return( err );
15728 }
15729
15730 static void _RegistrationTestSignalHandler( void *inContext )
15731 {
15732 RegistrationTest * const test = (RegistrationTest *) inContext;
15733
15734 FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15735
15736 _RegistrationTestExit( test, kCanceledErr );
15737 }
15738
15739 //===========================================================================================================================
15740
15741 static void _RegistrationTestStop( RegistrationTest *inTest )
15742 {
15743 OSStatus err;
15744
15745 dispatch_source_forget( &inTest->timer );
15746 dispatch_source_forget( &inTest->sigSourceINT );
15747 dispatch_source_forget( &inTest->sigSourceTERM );
15748 _RegistrationSubtestForget( &inTest->subtest );
15749 if( inTest->computerNameSet )
15750 {
15751 err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
15752 check_noerr( err );
15753 if( !err ) inTest->computerNameSet = false;
15754 }
15755 if( inTest->localHostNameSet )
15756 {
15757 err = _SetLocalHostName( inTest->localHostNamePrev );
15758 check_noerr( err );
15759 if( !err ) inTest->localHostNameSet = false;
15760 }
15761 }
15762
15763 //===========================================================================================================================
15764
15765 #define kRegistrationTestSubtestDurationSecs 5
15766
15767 static OSStatus
15768 _RegistrationTestStartSubtest(
15769 RegistrationTest * inTest,
15770 const RegistrationSubtestParams * inParams,
15771 Boolean * outSkipped )
15772 {
15773 OSStatus err;
15774 RegistrationSubtest * subtest;
15775 const char * interfaceDesc;
15776 DNSServiceFlags flags;
15777 char tag[ 6 + 1 ];
15778
15779 subtest = NULL;
15780 err = _RegistrationSubtestCreate( &subtest );
15781 require_noerr( err, exit );
15782
15783 subtest->test = inTest;
15784 subtest->useDefaultName = inParams->useDefaultName;
15785 subtest->useLODiscovery = inParams->useLODiscovery;
15786
15787 // Determine registration interfaces.
15788
15789 switch( inParams->interfaceSet )
15790 {
15791 case kRegistrationInterfaceSet_All:
15792 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15793
15794 if( !subtest->useLODiscovery )
15795 {
15796 err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
15797 require_noerr( err, exit );
15798 }
15799 interfaceDesc = "all interfaces (excluding AWDL)";
15800 break;
15801
15802 case kRegistrationInterfaceSet_AllPlusAWDL:
15803 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15804 subtest->includeAWDL = true;
15805
15806 if( !subtest->useLODiscovery )
15807 {
15808 err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
15809 require_noerr( err, exit );
15810 }
15811 interfaceDesc = "all interfaces (including AWDL)";
15812 break;
15813
15814 case kRegistrationInterfaceSet_LoopbackOnly:
15815 subtest->ifIndex = if_nametoindex( "lo0" );
15816 if( subtest->ifIndex == 0 )
15817 {
15818 FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
15819 err = kNoResourcesErr;
15820 goto exit;
15821 }
15822 interfaceDesc = "loopback interface";
15823 break;
15824
15825 case kRegistrationInterfaceSet_AWDLOnly:
15826 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
15827 if( err == kNotFoundErr )
15828 {
15829 FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
15830 subtest->skipped = true;
15831 err = kNoErr;
15832 }
15833 require_noerr( err, exit );
15834
15835 subtest->ifIsAWDL = true;
15836 interfaceDesc = "AWDL interface";
15837 break;
15838
15839 default:
15840 err = kParamErr;
15841 goto exit;
15842 }
15843
15844 // Create description.
15845
15846 ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
15847 interfaceDesc, subtest->useDefaultName ? "default" : "custom",
15848 subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
15849 require_action( subtest->description, exit, err = kNoMemoryErr );
15850
15851 if( subtest->skipped )
15852 {
15853 subtest->startTime = NanoTimeGetCurrent();
15854 }
15855 else
15856 {
15857 // Generate a service name.
15858
15859 if( subtest->useDefaultName )
15860 {
15861 subtest->serviceName = inTest->computerName;
15862 }
15863 else
15864 {
15865 ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
15866 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15867 require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
15868
15869 subtest->serviceName = subtest->serviceNameCustom;
15870 }
15871
15872 // Generate a service type.
15873
15874 ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
15875 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15876 require_action( subtest->serviceType, exit, err = kNoMemoryErr );
15877
15878 subtest->serviceTypeLen = strlen( subtest->serviceType );
15879
15880 // Create SRV and TXT record name FQDN.
15881
15882 ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
15883 require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
15884
15885 // Generate a port number.
15886
15887 subtest->port = (uint16_t) RandomRange( 60000, 65535 );
15888
15889 // Generate TXT record data.
15890
15891 err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
15892 require_noerr( err, exit );
15893
15894 // Register service.
15895
15896 subtest->startTime = NanoTimeGetCurrent();
15897
15898 flags = kDNSServiceFlagsNoAutoRename;
15899 if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
15900 err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
15901 subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
15902 NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
15903 _RegistrationSubtestRegisterCallback, subtest );
15904 require_noerr( err, exit );
15905
15906 err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
15907 require_noerr( err, exit );
15908
15909 // Start timer.
15910
15911 check( !inTest->timer );
15912 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
15913 INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15914 _RegistrationTestTimerHandler, inTest, &inTest->timer );
15915 require_noerr( err, exit );
15916 dispatch_resume( inTest->timer );
15917 }
15918
15919 *outSkipped = subtest->skipped;
15920
15921 check( !inTest->subtest );
15922 inTest->subtest = subtest;
15923 subtest = NULL;
15924
15925 exit:
15926 _RegistrationSubtestForget( &subtest );
15927 return( err );
15928 }
15929
15930 //===========================================================================================================================
15931
15932 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15933 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15934 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15935 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15936 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15937 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15938 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15939 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15940 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15941 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15942 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15943 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15944 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15945 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15946 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15947 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15948 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15949 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15950 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15951 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15952 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15953 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15954 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15955 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15956 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15957 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15958 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15959 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15960 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15961 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15962
15963 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15964 #define kRegistrationTestResultType_Query CFSTR( "query" )
15965 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15966 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15967 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15968
15969 static OSStatus
15970 _RegistrationTestAppendMissingResults(
15971 CFMutableArrayRef inMissingResults,
15972 const RegistrationResultTimes * inTimes,
15973 uint32_t inIfIndex,
15974 const char * inIfName );
15975
15976 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest )
15977 {
15978 OSStatus err;
15979 RegistrationSubtest * subtest;
15980 CFMutableDictionaryRef subtestReport;
15981 CFMutableArrayRef missing;
15982 char * txtStr;
15983 NanoTime64 now;
15984 Boolean subtestFailed;
15985 char startTime[ 32 ];
15986 char endTime[ 32 ];
15987 char ifNameBuf[ IF_NAMESIZE + 1 ];
15988
15989 now = NanoTimeGetCurrent();
15990
15991 subtest = inTest->subtest;
15992 inTest->subtest = NULL;
15993 _RegistrationSubtestStop( subtest );
15994
15995 missing = NULL;
15996 subtestReport = NULL;
15997 txtStr = NULL;
15998 if( subtest->txtPtr )
15999 {
16000 err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, NULL, 0, &txtStr );
16001 require_noerr( err, exit );
16002 }
16003 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16004 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16005 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
16006 "{"
16007 "%kO=%s" // description
16008 "%kO=%s" // startTime
16009 "%kO=%s" // endTime
16010 "%kO=%s" // serviceFQDN
16011 "%kO=%lli" // ifIndex
16012 "%kO=%s" // ifName
16013 "%kO=%lli" // port
16014 "%kO=%s" // txt
16015 "%kO=%b" // registered
16016 "%kO=%b" // usedDefaultName
16017 "%kO=%b" // usedLODiscovery
16018 "}",
16019 kRegistrationTestReportKey_Description, subtest->description,
16020 kRegistrationTestReportKey_StartTime, startTime,
16021 kRegistrationTestReportKey_EndTime, endTime,
16022 kRegistrationTestReportKey_ServiceFQDN, subtest->serviceFQDN,
16023 kRegistrationTestReportKey_InterfaceIndex, (int64_t) subtest->ifIndex,
16024 kRegistrationTestReportKey_InterfaceName, if_indextoname( subtest->ifIndex, ifNameBuf ),
16025 kRegistrationTestReportKey_Port, (int64_t) subtest->port,
16026 kRegistrationTestReportKey_TXT, txtStr,
16027 kRegistrationTestReportKey_Registered, (int) subtest->registered,
16028 kRegistrationTestReportKey_UsedDefaultName, (int) subtest->useDefaultName,
16029 kRegistrationTestReportKey_UsedLODiscovery, (int) subtest->useLODiscovery );
16030 ForgetMem( &txtStr );
16031 require_noerr( err, exit );
16032
16033 if( !subtest->skipped && subtest->registered )
16034 {
16035 missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16036 require_action( missing, exit, err = kNoMemoryErr );
16037
16038 if( subtest->ifList )
16039 {
16040 RegistrationInterfaceItem * item;
16041
16042 for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16043 {
16044 #if( TARGET_OS_WATCH )
16045 if( inTest->forBATS && item->base.isWiFi ) continue;
16046 #endif
16047 err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
16048 require_noerr( err, exit );
16049 }
16050 }
16051 else
16052 {
16053 err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
16054 require_noerr( err, exit );
16055 }
16056
16057 subtestFailed = false;
16058 if( CFArrayGetCount( missing ) > 0 )
16059 {
16060 subtestFailed = true;
16061 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
16062 }
16063 if( CFArrayGetCount( subtest->unexpected ) > 0 )
16064 {
16065 subtestFailed = true;
16066 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
16067 }
16068 #if( TARGET_OS_WATCH )
16069 if( CFArrayGetCount( subtest->ignored ) > 0 )
16070 {
16071 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
16072 }
16073 #endif
16074 }
16075 else
16076 {
16077 subtestFailed = true;
16078 }
16079
16080 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
16081 if( subtestFailed )
16082 {
16083 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
16084 if( !subtest->skipped ) inTest->failed = true;
16085 }
16086 CFArrayAppendValue( inTest->subtestReports, subtestReport );
16087
16088 exit:
16089 CFReleaseNullSafe( missing );
16090 CFReleaseNullSafe( subtestReport );
16091 _RegistrationSubtestFree( subtest );
16092 return( err );
16093 }
16094
16095 static OSStatus
16096 _RegistrationTestAppendMissingResult(
16097 CFMutableArrayRef inMissingResults,
16098 CFStringRef inType,
16099 uint32_t inIfIndex,
16100 const char * inIfName );
16101
16102 static OSStatus
16103 _RegistrationTestAppendMissingResults(
16104 CFMutableArrayRef inMissingResults,
16105 const RegistrationResultTimes * inTimes,
16106 uint32_t inIfIndex,
16107 const char * inIfName )
16108 {
16109 OSStatus err;
16110
16111 if( !inTimes->browseResultTime )
16112 {
16113 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
16114 inIfIndex, inIfName );
16115 require_noerr( err, exit );
16116 }
16117 if( !inTimes->querySRVResultTime )
16118 {
16119 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
16120 inIfIndex, inIfName );
16121 require_noerr( err, exit );
16122 }
16123 if( !inTimes->queryTXTResultTime )
16124 {
16125 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
16126 inIfIndex, inIfName );
16127 require_noerr( err, exit );
16128 }
16129 err = kNoErr;
16130
16131 exit:
16132 return( err );
16133 }
16134
16135 static OSStatus
16136 _RegistrationTestAppendMissingResult(
16137 CFMutableArrayRef inMissingResults,
16138 CFStringRef inType,
16139 uint32_t inIfIndex,
16140 const char * inIfName )
16141 {
16142 OSStatus err;
16143 char ifName[ IF_NAMESIZE + 1 ];
16144
16145 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
16146 "{"
16147 "%kO=%O" // resultType
16148 "%kO=%lli" // ifIndex
16149 "%kO=%s" // ifName
16150 "}",
16151 kRegistrationTestReportKey_ResultType, inType,
16152 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16153 kRegistrationTestReportKey_InterfaceName, inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
16154 return( err );
16155 }
16156
16157 //===========================================================================================================================
16158
16159 static void _RegistrationTestEnd( RegistrationTest *inTest )
16160 {
16161 OSStatus err;
16162 NanoTime64 now;
16163 CFPropertyListRef plist;
16164 char startTime[ 32 ];
16165 char endTime[ 32 ];
16166
16167 now = NanoTimeGetCurrent();
16168 _NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
16169 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16170
16171 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16172 "{"
16173 "%kO=%s" // startTime
16174 "%kO=%s" // endTime
16175 "%kO=%s" // computerName
16176 "%kO=%s" // localHostName
16177 "%kO=%O" // subtests
16178 "%kO=%b" // pass
16179 "}",
16180 kRegistrationTestReportKey_StartTime, startTime,
16181 kRegistrationTestReportKey_EndTime, endTime,
16182 kRegistrationTestReportKey_ComputerName, inTest->computerName,
16183 kRegistrationTestReportKey_LocalHostName, inTest->localHostName,
16184 kRegistrationTestReportKey_Subtests, inTest->subtestReports,
16185 kRegistrationTestReportKey_Pass, inTest->failed ? false : true );
16186 require_noerr( err, exit );
16187
16188 err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
16189 CFRelease( plist );
16190 require_noerr( err, exit );
16191
16192 exit:
16193 _RegistrationTestExit( inTest, err );
16194 }
16195
16196 //===========================================================================================================================
16197
16198 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
16199 {
16200 int exitCode;
16201
16202 if( inError )
16203 {
16204 FPrintF( stderr, "error: %#m\n", inError );
16205 exitCode = 1;
16206 }
16207 else
16208 {
16209 exitCode = inTest->failed ? 2 : 0;
16210 }
16211 _RegistrationTestForget( &inTest );
16212 exit( exitCode );
16213 }
16214
16215 //===========================================================================================================================
16216
16217 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
16218 {
16219 OSStatus err;
16220 RegistrationSubtest * obj;
16221
16222 obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
16223 require_action( obj, exit, err = kNoMemoryErr );
16224
16225 obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16226 require_action( obj->unexpected, exit, err = kNoMemoryErr );
16227
16228 #if( TARGET_OS_WATCH )
16229 obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16230 require_action( obj->ignored, exit, err = kNoMemoryErr );
16231 #endif
16232
16233 *outSubtest = obj;
16234 obj = NULL;
16235 err = kNoErr;
16236
16237 exit:
16238 if( obj ) _RegistrationSubtestFree( obj );
16239 return( err );
16240 }
16241
16242 //===========================================================================================================================
16243
16244 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest )
16245 {
16246 check( !inSubtest->registration );
16247 check( !inSubtest->browse );
16248 check( !inSubtest->querySRV );
16249 check( !inSubtest->queryTXT );
16250 check( !inSubtest->connection );
16251 ForgetMem( &inSubtest->serviceNameCustom );
16252 ForgetMem( &inSubtest->serviceType );
16253 ForgetMem( &inSubtest->serviceFQDN );
16254 ForgetMem( &inSubtest->txtPtr );
16255 ForgetCF( &inSubtest->unexpected );
16256 #if( TARGET_OS_WATCH )
16257 ForgetCF( &inSubtest->ignored );
16258 #endif
16259 _MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
16260 ForgetMem( &inSubtest->description );
16261 free( inSubtest );
16262 }
16263
16264 //===========================================================================================================================
16265
16266 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest )
16267 {
16268 DNSServiceForget( &inSubtest->registration );
16269 DNSServiceForget( &inSubtest->browse );
16270 DNSServiceForget( &inSubtest->querySRV );
16271 DNSServiceForget( &inSubtest->queryTXT );
16272 DNSServiceForget( &inSubtest->connection );
16273 }
16274
16275 //===========================================================================================================================
16276
16277 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
16278 {
16279 OSStatus err;
16280 RegistrationInterfaceItem * list;
16281 const MDNSInterfaceSubset subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
16282
16283 err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
16284 require_noerr( err, exit );
16285
16286 *outList = list;
16287
16288 exit:
16289 return( err );
16290 }
16291
16292 //===========================================================================================================================
16293
16294 static OSStatus
16295 _RegistrationTestCreateRandomTXTRecord(
16296 size_t inMinLen,
16297 size_t inMaxLen,
16298 uint8_t ** outTXTPtr,
16299 size_t * outTXTLen )
16300 {
16301 OSStatus err;
16302 uint8_t * ptr;
16303 const uint8_t * txtEnd;
16304 uint8_t * txtPtr = NULL;
16305 size_t txtLen;
16306
16307 require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
16308
16309 txtLen = RandomRange( inMinLen, inMaxLen );
16310 txtPtr = (uint8_t *) malloc( txtLen + 1 );
16311 require_action( txtPtr, exit, err = kNoMemoryErr );
16312
16313 _RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
16314
16315 ptr = txtPtr;
16316 txtEnd = &txtPtr[ txtLen ];
16317 while( ptr < txtEnd )
16318 {
16319 size_t maxLen, len;
16320
16321 maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
16322 len = RandomRange( 1, 255 );
16323 if( len > maxLen ) len = maxLen;
16324
16325 *ptr = (uint8_t) len;
16326 ptr += ( 1 + len );
16327 }
16328 check( ptr == txtEnd );
16329
16330 if( outTXTPtr )
16331 {
16332 *outTXTPtr = txtPtr;
16333 txtPtr = NULL;
16334 }
16335 if( outTXTLen ) *outTXTLen = txtLen;
16336 err = kNoErr;
16337
16338 exit:
16339 FreeNullSafe( txtPtr );
16340 return( err );
16341 }
16342
16343 //===========================================================================================================================
16344
16345 static void DNSSD_API
16346 _RegistrationSubtestRegisterCallback(
16347 DNSServiceRef inSDRef,
16348 DNSServiceFlags inFlags,
16349 DNSServiceErrorType inError,
16350 const char * inServiceName,
16351 const char * inServiceType,
16352 const char * inDomain,
16353 void * inContext )
16354 {
16355 OSStatus err;
16356 const NanoTime64 now = NanoTimeGetCurrent();
16357 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16358
16359 Unused( inSDRef );
16360
16361 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
16362 ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16363 _RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
16364 ( strcasecmp( inDomain, "local." ) == 0 ) )
16365 {
16366 if( !subtest->registered )
16367 {
16368 DNSServiceRef sdRef;
16369 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16370
16371 subtest->registered = true;
16372
16373 // Create shared connection.
16374
16375 check( !subtest->connection );
16376 err = DNSServiceCreateConnection( &subtest->connection );
16377 require_noerr( err, exit );
16378
16379 err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
16380 require_noerr( err, exit );
16381
16382 // Start browse.
16383
16384 check( !subtest->browse );
16385 sdRef = subtest->connection;
16386 err = DNSServiceBrowse( &sdRef, flags,
16387 subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
16388 subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
16389 require_noerr( err, exit );
16390
16391 subtest->browse = sdRef;
16392 }
16393 }
16394 else
16395 {
16396 char timestamp[ 32 ];
16397
16398 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
16399 "{"
16400 "%kO=%O" // resultType
16401 "%kO=%s" // timestamp
16402 "%kO=%lli" // flags
16403 "%kO=%lli" // error
16404 "%kO=%s" // serviceName
16405 "%kO=%s" // serviceType
16406 "%kO=%s" // domain
16407 "}",
16408 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Registration,
16409 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16410 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16411 kRegistrationTestReportKey_Error, (int64_t) inError,
16412 kRegistrationTestReportKey_ServiceName, inServiceName,
16413 kRegistrationTestReportKey_ServiceType, inServiceType,
16414 kRegistrationTestReportKey_Domain, inDomain );
16415 require_noerr( err, exit );
16416 }
16417 err = kNoErr;
16418
16419 exit:
16420 if( err ) _RegistrationTestExit( subtest->test, err );
16421 }
16422
16423 //===========================================================================================================================
16424
16425 static void DNSSD_API
16426 _RegistrationSubtestBrowseCallback(
16427 DNSServiceRef inSDRef,
16428 DNSServiceFlags inFlags,
16429 uint32_t inIfIndex,
16430 DNSServiceErrorType inError,
16431 const char * inServiceName,
16432 const char * inServiceType,
16433 const char * inDomain,
16434 void * inContext )
16435 {
16436 OSStatus err;
16437 NanoTime64 now;
16438 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16439 Boolean serviceIsCorrect, resultIsExpected;
16440
16441 Unused( inSDRef );
16442
16443 now = NanoTimeGetCurrent();
16444 if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16445 _RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
16446 {
16447 serviceIsCorrect = true;
16448 }
16449 else
16450 {
16451 serviceIsCorrect = false;
16452 }
16453
16454 resultIsExpected = false;
16455 if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
16456 {
16457 RegistrationResultTimes * times;
16458
16459 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
16460 if( times )
16461 {
16462 DNSServiceRef sdRef;
16463 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16464 uint32_t ifIndex;
16465
16466 resultIsExpected = true;
16467 if( !times->browseResultTime ) times->browseResultTime = now;
16468
16469 ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
16470 if( !subtest->querySRV )
16471 {
16472 // Start SRV record query.
16473
16474 sdRef = subtest->connection;
16475 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
16476 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16477 require_noerr( err, exit );
16478
16479 subtest->querySRV = sdRef;
16480 }
16481 if( !subtest->queryTXT )
16482 {
16483 // Start TXT record query.
16484
16485 sdRef = subtest->connection;
16486 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
16487 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16488 require_noerr( err, exit );
16489
16490 subtest->queryTXT = sdRef;
16491 }
16492 }
16493 }
16494
16495 if( !resultIsExpected )
16496 {
16497 CFMutableArrayRef resultArray;
16498 char timestamp[ 32 ];
16499 const char * ifNamePtr;
16500 char ifNameBuf[ IF_NAMESIZE + 1 ];
16501
16502 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16503 resultArray = subtest->unexpected;
16504 #if( TARGET_OS_WATCH )
16505 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16506 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
16507 {
16508 resultArray = subtest->ignored;
16509 }
16510 #endif
16511 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
16512 "{"
16513 "%kO=%O" // resultType
16514 "%kO=%s" // timestamp
16515 "%kO=%lli" // flags
16516 "%kO=%lli" // ifIndex
16517 "%kO=%s" // ifName
16518 "%kO=%lli" // error
16519 "%kO=%s" // serviceName
16520 "%kO=%s" // serviceType
16521 "%kO=%s" // domain
16522 "}",
16523 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Browse,
16524 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16525 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16526 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16527 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16528 kRegistrationTestReportKey_Error, (int64_t) inError,
16529 kRegistrationTestReportKey_ServiceName, inServiceName,
16530 kRegistrationTestReportKey_ServiceType, inServiceType,
16531 kRegistrationTestReportKey_Domain, inDomain );
16532 require_noerr( err, exit );
16533 }
16534 err = kNoErr;
16535
16536 exit:
16537 if( err ) _RegistrationTestExit( subtest->test, err );
16538 }
16539
16540 //===========================================================================================================================
16541
16542 static Boolean
16543 _RegistrationSubtestIsSRVRecordDataValid(
16544 RegistrationSubtest * inSubtest,
16545 const uint8_t * inRDataPtr,
16546 size_t inRDataLen,
16547 Boolean inExpectRandHostname );
16548
16549 static void DNSSD_API
16550 _RegistrationSubtestQueryCallback(
16551 DNSServiceRef inSDRef,
16552 DNSServiceFlags inFlags,
16553 uint32_t inIfIndex,
16554 DNSServiceErrorType inError,
16555 const char * inName,
16556 uint16_t inType,
16557 uint16_t inClass,
16558 uint16_t inRDataLen,
16559 const void * inRDataPtr,
16560 uint32_t inTTL,
16561 void * inContext )
16562 {
16563 OSStatus err;
16564 NanoTime64 now;
16565 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16566 Boolean resultIsExpected;
16567
16568 Unused( inSDRef );
16569 Unused( inTTL );
16570
16571 now = NanoTimeGetCurrent();
16572 resultIsExpected = false;
16573 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
16574 ( inClass == kDNSServiceClass_IN ) )
16575 {
16576 RegistrationResultTimes * times;
16577 Boolean isAWDL;
16578
16579 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
16580 if( times )
16581 {
16582 if( inType == kDNSServiceType_SRV )
16583 {
16584 Boolean expectRandHostname;
16585
16586 if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
16587 {
16588 expectRandHostname = true;
16589 }
16590 else
16591 {
16592 expectRandHostname = false;
16593 }
16594 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16595 {
16596 resultIsExpected = true;
16597 if( !times->querySRVResultTime ) times->querySRVResultTime = now;
16598 }
16599 }
16600 else if( inType == kDNSServiceType_TXT )
16601 {
16602 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16603 {
16604 resultIsExpected = true;
16605 if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
16606 }
16607 }
16608 }
16609 }
16610
16611 if( !resultIsExpected )
16612 {
16613 CFMutableArrayRef resultArray;
16614 CFMutableDictionaryRef resultDict;
16615 CFStringRef rdataKey;
16616 char * rdataStr;
16617 const char * ifNamePtr;
16618 char timestamp[ 32 ];
16619 char ifNameBuf[ IF_NAMESIZE + 1 ];
16620
16621 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16622 resultArray = subtest->unexpected;
16623 #if( TARGET_OS_WATCH )
16624 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16625 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
16626 ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
16627 {
16628 if( inType == kDNSServiceType_SRV )
16629 {
16630 const Boolean expectRandHostname = subtest->includeAWDL ? true : false;
16631
16632 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16633 {
16634 resultArray = subtest->ignored;
16635 }
16636 }
16637 else if( inType == kDNSServiceType_TXT )
16638 {
16639 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16640 {
16641 resultArray = subtest->ignored;
16642 }
16643 }
16644 }
16645 #endif
16646 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
16647 "{"
16648 "%kO=%O" // resultType
16649 "%kO=%s" // timestamp
16650 "%kO=%lli" // flags
16651 "%kO=%lli" // ifIndex
16652 "%kO=%s" // ifName
16653 "%kO=%lli" // error
16654 "%kO=%s" // serviceFQDN
16655 "%kO=%lli" // recordType
16656 "%kO=%lli" // class
16657 "}",
16658 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Query,
16659 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16660 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16661 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16662 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16663 kRegistrationTestReportKey_Error, (int64_t) inError,
16664 kRegistrationTestReportKey_ServiceFQDN, inName,
16665 kRegistrationTestReportKey_RecordType, (int64_t) inType,
16666 kRegistrationTestReportKey_RecordClass, (int64_t) inClass );
16667 require_noerr( err, exit );
16668
16669 rdataStr = NULL;
16670 DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
16671 if( rdataStr )
16672 {
16673 rdataKey = kRegistrationTestReportKey_RDataFormatted;
16674 }
16675 else
16676 {
16677 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16678 require_action( rdataStr, exit, err = kNoMemoryErr );
16679
16680 rdataKey = kRegistrationTestReportKey_RDataHexString;
16681 }
16682 err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
16683 ForgetMem( &rdataStr );
16684 if( err ) CFRelease( resultDict );
16685 require_noerr( err, exit );
16686
16687 CFArrayAppendValue( resultArray, resultDict );
16688 CFRelease( resultDict );
16689 }
16690 err = kNoErr;
16691
16692 exit:
16693 if( err ) _RegistrationTestExit( subtest->test, err );
16694 }
16695
16696 static Boolean
16697 _RegistrationSubtestIsSRVRecordDataValid(
16698 RegistrationSubtest * inSubtest,
16699 const uint8_t * inRDataPtr,
16700 size_t inRDataLen,
16701 Boolean inExpectRandHostname )
16702 {
16703 const dns_fixed_fields_srv * fields;
16704 const uint8_t * const end = &inRDataPtr[ inRDataLen ];
16705 const uint8_t * label;
16706 size_t len;
16707 uint16_t port;
16708 Boolean isValid;
16709
16710 isValid = false;
16711 require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
16712
16713 fields = (const dns_fixed_fields_srv *) inRDataPtr;
16714 port = dns_fixed_fields_srv_get_port( fields );
16715 require_quiet( port == inSubtest->port, exit );
16716
16717 // First target label should be a UUID string for the AWDL interface.
16718
16719 label = (const uint8_t *) &fields[ 1 ];
16720 require_quiet( ( end - label ) >= 1, exit );
16721
16722 len = label[ 0 ];
16723 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16724
16725 if( inExpectRandHostname )
16726 {
16727 if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
16728 }
16729 else
16730 {
16731 if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
16732 }
16733
16734 // Second target label should be "local".
16735
16736 label = &label[ 1 + len ];
16737 require_quiet( ( end - label ) >= 1, exit );
16738
16739 len = label[ 0 ];
16740 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16741
16742 if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
16743
16744 // Third target label should be the root label.
16745
16746 label = &label[ 1 + len ];
16747 require_quiet( ( end - label ) >= 1, exit );
16748
16749 len = label[ 0 ];
16750 if( len != 0 ) goto exit;
16751
16752 isValid = true;
16753
16754 exit:
16755 return( isValid );
16756 }
16757
16758 //===========================================================================================================================
16759
16760 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
16761 {
16762 if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
16763 {
16764 const char * const ptr = &inServiceType[ inSubtest->serviceTypeLen ];
16765
16766 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
16767 }
16768 return( false );
16769 }
16770
16771 //===========================================================================================================================
16772
16773 static RegistrationResultTimes *
16774 _RegistrationSubtestGetInterfaceResultTimes(
16775 RegistrationSubtest * inSubtest,
16776 uint32_t inIfIndex,
16777 Boolean * outIsAWDL )
16778 {
16779 if( inSubtest->ifList )
16780 {
16781 RegistrationInterfaceItem * item;
16782
16783 for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16784 {
16785 if( inIfIndex == item->base.ifIndex )
16786 {
16787 if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
16788 return( &item->times );
16789 }
16790 }
16791 }
16792 else
16793 {
16794 if( inIfIndex == inSubtest->ifIndex )
16795 {
16796 if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
16797 return( &inSubtest->ifTimes );
16798 }
16799 }
16800 return( NULL );
16801 }
16802
16803 //===========================================================================================================================
16804
16805 static void _RegistrationTestTimerHandler( void *inContext )
16806 {
16807 RegistrationTest * const test = (RegistrationTest *) inContext;
16808
16809 dispatch_source_forget( &test->timer );
16810 _RegistrationTestProceed( test );
16811 }
16812
16813 //===========================================================================================================================
16814
16815 #if( TARGET_OS_WATCH )
16816 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName )
16817 {
16818 NetTransportType type = kNetTransportType_Undefined;
16819
16820 SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
16821 return( ( type == kNetTransportType_WiFi ) ? true : false );
16822 }
16823 #endif
16824
16825 //===========================================================================================================================
16826 // SSDPDiscoverCmd
16827 //===========================================================================================================================
16828
16829 #define kSSDPPort 1900
16830
16831 typedef struct
16832 {
16833 HTTPHeader header; // HTTP header object for sending and receiving.
16834 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
16835 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
16836 int receiveSecs; // After send, the amount of time to spend receiving.
16837 uint32_t ifindex; // Index of the interface over which to send the query.
16838 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
16839 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
16840
16841 } SSDPDiscoverContext;
16842
16843 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
16844 static void SSDPDiscoverReadHandler( void *inContext );
16845 static int SocketToPortNumber( SocketRef inSock );
16846 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
16847
16848 static void SSDPDiscoverCmd( void )
16849 {
16850 OSStatus err;
16851 struct timeval now;
16852 SSDPDiscoverContext * context;
16853 dispatch_source_t signalSource = NULL;
16854 SocketRef sockV4 = kInvalidSocketRef;
16855 SocketRef sockV6 = kInvalidSocketRef;
16856 ssize_t n;
16857 int sendCount;
16858
16859 // Set up SIGINT handler.
16860
16861 signal( SIGINT, SIG_IGN );
16862 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
16863 require_noerr( err, exit );
16864 dispatch_resume( signalSource );
16865
16866 // Check command parameters.
16867
16868 if( gSSDPDiscover_ReceiveSecs < -1 )
16869 {
16870 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
16871 err = kParamErr;
16872 goto exit;
16873 }
16874
16875 // Create context.
16876
16877 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
16878 require_action( context, exit, err = kNoMemoryErr );
16879
16880 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
16881 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
16882 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
16883
16884 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
16885 require_noerr_quiet( err, exit );
16886
16887 // Set up IPv4 socket.
16888
16889 if( context->useIPv4 )
16890 {
16891 int port;
16892 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
16893 require_noerr( err, exit );
16894
16895 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
16896 require_noerr( err, exit );
16897
16898 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
16899 err = map_socket_noerr_errno( sockV4, err );
16900 require_noerr( err, exit );
16901 }
16902
16903 // Set up IPv6 socket.
16904
16905 if( context->useIPv6 )
16906 {
16907 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
16908 require_noerr( err, exit );
16909
16910 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
16911 require_noerr( err, exit );
16912
16913 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
16914 err = map_socket_noerr_errno( sockV6, err );
16915 require_noerr( err, exit );
16916 }
16917
16918 // Print prologue.
16919
16920 SSDPDiscoverPrintPrologue( context );
16921
16922 // Send mDNS query message.
16923
16924 sendCount = 0;
16925 if( IsValidSocket( sockV4 ) )
16926 {
16927 struct sockaddr_in mcastAddr4;
16928
16929 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
16930 SIN_LEN_SET( &mcastAddr4 );
16931 mcastAddr4.sin_family = AF_INET;
16932 mcastAddr4.sin_port = htons( kSSDPPort );
16933 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
16934
16935 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
16936 require_noerr( err, exit );
16937
16938 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
16939 (socklen_t) sizeof( mcastAddr4 ) );
16940 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
16941 if( err )
16942 {
16943 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
16944 ForgetSocket( &sockV4 );
16945 }
16946 else
16947 {
16948 if( gSSDPDiscover_Verbose )
16949 {
16950 gettimeofday( &now, NULL );
16951 FPrintF( stdout, "---\n" );
16952 FPrintF( stdout, "Send time: %{du:time}\n", &now );
16953 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
16954 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
16955 FPrintF( stdout, "Message size: %zu\n", context->header.len );
16956 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
16957 }
16958 ++sendCount;
16959 }
16960 }
16961
16962 if( IsValidSocket( sockV6 ) )
16963 {
16964 struct sockaddr_in6 mcastAddr6;
16965
16966 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
16967 SIN6_LEN_SET( &mcastAddr6 );
16968 mcastAddr6.sin6_family = AF_INET6;
16969 mcastAddr6.sin6_port = htons( kSSDPPort );
16970 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16971 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
16972 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
16973
16974 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
16975 require_noerr( err, exit );
16976
16977 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
16978 (socklen_t) sizeof( mcastAddr6 ) );
16979 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
16980 if( err )
16981 {
16982 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
16983 ForgetSocket( &sockV6 );
16984 }
16985 else
16986 {
16987 if( gSSDPDiscover_Verbose )
16988 {
16989 gettimeofday( &now, NULL );
16990 FPrintF( stdout, "---\n" );
16991 FPrintF( stdout, "Send time: %{du:time}\n", &now );
16992 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
16993 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
16994 FPrintF( stdout, "Message size: %zu\n", context->header.len );
16995 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
16996 }
16997 ++sendCount;
16998 }
16999 }
17000 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
17001
17002 // If there's no wait period after the send, then exit.
17003
17004 if( context->receiveSecs == 0 ) goto exit;
17005
17006 // Create dispatch read sources for socket(s).
17007
17008 if( IsValidSocket( sockV4 ) )
17009 {
17010 SocketContext * sockCtx;
17011
17012 err = SocketContextCreate( sockV4, context, &sockCtx );
17013 require_noerr( err, exit );
17014 sockV4 = kInvalidSocketRef;
17015
17016 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17017 &context->readSourceV4 );
17018 if( err ) ForgetSocketContext( &sockCtx );
17019 require_noerr( err, exit );
17020
17021 dispatch_resume( context->readSourceV4 );
17022 }
17023
17024 if( IsValidSocket( sockV6 ) )
17025 {
17026 SocketContext * sockCtx;
17027
17028 err = SocketContextCreate( sockV6, context, &sockCtx );
17029 require_noerr( err, exit );
17030 sockV6 = kInvalidSocketRef;
17031
17032 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17033 &context->readSourceV6 );
17034 if( err ) ForgetSocketContext( &sockCtx );
17035 require_noerr( err, exit );
17036
17037 dispatch_resume( context->readSourceV6 );
17038 }
17039
17040 if( context->receiveSecs > 0 )
17041 {
17042 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
17043 Exit );
17044 }
17045 dispatch_main();
17046
17047 exit:
17048 ForgetSocket( &sockV4 );
17049 ForgetSocket( &sockV6 );
17050 dispatch_source_forget( &signalSource );
17051 exit( err ? 1 : 0 );
17052 }
17053
17054 static int SocketToPortNumber( SocketRef inSock )
17055 {
17056 OSStatus err;
17057 sockaddr_ip sip;
17058 socklen_t len;
17059
17060 len = (socklen_t) sizeof( sip );
17061 err = getsockname( inSock, &sip.sa, &len );
17062 err = map_socket_noerr_errno( inSock, err );
17063 check_noerr( err );
17064 return( err ? -1 : SockAddrGetPort( &sip ) );
17065 }
17066
17067 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
17068 {
17069 OSStatus err;
17070
17071 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
17072 require_noerr( err, exit );
17073
17074 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
17075 require_noerr( err, exit );
17076
17077 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
17078 require_noerr( err, exit );
17079
17080 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
17081 require_noerr( err, exit );
17082
17083 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
17084 require_noerr( err, exit );
17085
17086 err = HTTPHeader_Commit( inHeader );
17087 require_noerr( err, exit );
17088
17089 exit:
17090 return( err );
17091 }
17092
17093 //===========================================================================================================================
17094 // SSDPDiscoverPrintPrologue
17095 //===========================================================================================================================
17096
17097 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
17098 {
17099 const int receiveSecs = inContext->receiveSecs;
17100 const char * ifName;
17101 char ifNameBuf[ IF_NAMESIZE + 1 ];
17102 NetTransportType ifType;
17103
17104 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
17105
17106 ifType = kNetTransportType_Undefined;
17107 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
17108
17109 FPrintF( stdout, "Interface: %s/%d/%s\n",
17110 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
17111 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
17112 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
17113 FPrintF( stdout, "Receive duration: " );
17114 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
17115 else FPrintF( stdout, "∞\n" );
17116 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17117 }
17118
17119 //===========================================================================================================================
17120 // SSDPDiscoverReadHandler
17121 //===========================================================================================================================
17122
17123 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader );
17124
17125 static void SSDPDiscoverReadHandler( void *inContext )
17126 {
17127 OSStatus err;
17128 struct timeval now;
17129 SocketContext * const sockCtx = (SocketContext *) inContext;
17130 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
17131 HTTPHeader * const header = &context->header;
17132 sockaddr_ip fromAddr;
17133 size_t msgLen;
17134
17135 gettimeofday( &now, NULL );
17136
17137 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
17138 NULL, NULL, NULL, NULL );
17139 require_noerr( err, exit );
17140
17141 FPrintF( stdout, "---\n" );
17142 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
17143 FPrintF( stdout, "Source: %##a\n", &fromAddr );
17144 FPrintF( stdout, "Message size: %zu\n", msgLen );
17145 header->len = msgLen;
17146 if( _HTTPHeader_Validate( header ) )
17147 {
17148 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
17149 if( header->extraDataLen > 0 )
17150 {
17151 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
17152 }
17153 }
17154 else
17155 {
17156 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
17157 goto exit;
17158 }
17159
17160 exit:
17161 if( err ) exit( 1 );
17162 }
17163
17164 //===========================================================================================================================
17165 // _HTTPHeader_Validate
17166 //
17167 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17168 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17169 //
17170 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17171 //===========================================================================================================================
17172
17173 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader )
17174 {
17175 const char * src;
17176 const char * end;
17177
17178 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17179
17180 require( inHeader->len < sizeof( inHeader->buf ), exit );
17181 src = inHeader->buf;
17182 end = src + inHeader->len;
17183 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
17184 {
17185 src += 4;
17186 }
17187 else
17188 {
17189 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17190 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17191
17192 for( ;; )
17193 {
17194 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
17195 if( src >= end ) goto exit;
17196 ++src;
17197 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17198 {
17199 src += 2;
17200 break;
17201 }
17202 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
17203 {
17204 src += 1;
17205 break;
17206 }
17207 }
17208 }
17209 inHeader->extraDataPtr = src;
17210 inHeader->extraDataLen = (size_t)( end - src );
17211 inHeader->len = (size_t)( src - inHeader->buf );
17212 return( true );
17213
17214 exit:
17215 return( false );
17216 }
17217
17218 #if( TARGET_OS_DARWIN )
17219 //===========================================================================================================================
17220 // ResQueryCmd
17221 //===========================================================================================================================
17222
17223 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17224
17225 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
17226 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
17227 int,
17228 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17229 ( dname, class, type, answer, anslen ) );
17230
17231 // res_query() from libinfo
17232
17233 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
17234 SOFT_LINK_FUNCTION_EX( info, res_query,
17235 int,
17236 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17237 ( dname, class, type, answer, anslen ) );
17238
17239 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
17240
17241 static void ResQueryCmd( void )
17242 {
17243 OSStatus err;
17244 res_query_f res_query_ptr;
17245 int n;
17246 uint16_t type, class;
17247 uint8_t answer[ 1024 ];
17248
17249 // Get pointer to one of the res_query() functions.
17250
17251 if( gResQuery_UseLibInfo )
17252 {
17253 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
17254 {
17255 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
17256 err = kNotFoundErr;
17257 goto exit;
17258 }
17259 res_query_ptr = soft_res_query;
17260 }
17261 else
17262 {
17263 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
17264 {
17265 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
17266 err = kNotFoundErr;
17267 goto exit;
17268 }
17269 res_query_ptr = soft_res_9_query;
17270 }
17271
17272 // Get record type.
17273
17274 err = RecordTypeFromArgString( gResQuery_Type, &type );
17275 require_noerr( err, exit );
17276
17277 // Get record class.
17278
17279 if( gResQuery_Class )
17280 {
17281 err = RecordClassFromArgString( gResQuery_Class, &class );
17282 require_noerr( err, exit );
17283 }
17284 else
17285 {
17286 class = kDNSServiceClass_IN;
17287 }
17288
17289 // Print prologue.
17290
17291 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
17292 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17293 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17294 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17295 FPrintF( stdout, "---\n" );
17296
17297 // Call res_query().
17298
17299 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
17300 if( n < 0 )
17301 {
17302 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17303 err = kUnknownErr;
17304 goto exit;
17305 }
17306
17307 // Print result.
17308
17309 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17310
17311 exit:
17312 if( err ) exit( 1 );
17313 }
17314
17315 //===========================================================================================================================
17316 // ResolvDNSQueryCmd
17317 //===========================================================================================================================
17318
17319 // 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
17320 // avoid including the header file.
17321
17322 typedef void * dns_handle_t;
17323
17324 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
17325 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
17326 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
17327 int32_t, (
17328 dns_handle_t dns,
17329 const char * name,
17330 uint32_t dnsclass,
17331 uint32_t dnstype,
17332 char * buf,
17333 uint32_t len,
17334 struct sockaddr * from,
17335 uint32_t * fromlen ),
17336 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
17337
17338 static void ResolvDNSQueryCmd( void )
17339 {
17340 OSStatus err;
17341 int n;
17342 dns_handle_t dns = NULL;
17343 uint16_t type, class;
17344 sockaddr_ip from;
17345 uint32_t fromLen;
17346 uint8_t answer[ 1024 ];
17347
17348 // Make sure that the required symbols are available.
17349
17350 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
17351 {
17352 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
17353 err = kNotFoundErr;
17354 goto exit;
17355 }
17356
17357 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
17358 {
17359 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
17360 err = kNotFoundErr;
17361 goto exit;
17362 }
17363
17364 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
17365 {
17366 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
17367 err = kNotFoundErr;
17368 goto exit;
17369 }
17370
17371 // Get record type.
17372
17373 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
17374 require_noerr( err, exit );
17375
17376 // Get record class.
17377
17378 if( gResolvDNSQuery_Class )
17379 {
17380 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
17381 require_noerr( err, exit );
17382 }
17383 else
17384 {
17385 class = kDNSServiceClass_IN;
17386 }
17387
17388 // Get dns handle.
17389
17390 dns = soft_dns_open( gResolvDNSQuery_Path );
17391 if( !dns )
17392 {
17393 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
17394 err = kUnknownErr;
17395 goto exit;
17396 }
17397
17398 // Print prologue.
17399
17400 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
17401 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17402 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17403 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
17404 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17405 FPrintF( stdout, "---\n" );
17406
17407 // Call dns_query().
17408
17409 memset( &from, 0, sizeof( from ) );
17410 fromLen = (uint32_t) sizeof( from );
17411 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
17412 &fromLen );
17413 if( n < 0 )
17414 {
17415 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17416 err = kUnknownErr;
17417 goto exit;
17418 }
17419
17420 // Print result.
17421
17422 FPrintF( stdout, "From: %##a\n", &from );
17423 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17424
17425 exit:
17426 if( dns ) soft_dns_free( dns );
17427 if( err ) exit( 1 );
17428 }
17429
17430 //===========================================================================================================================
17431 // CFHostCmd
17432 //===========================================================================================================================
17433
17434 static void
17435 _CFHostResolveCallback(
17436 CFHostRef inHost,
17437 CFHostInfoType inInfoType,
17438 const CFStreamError * inError,
17439 void * inInfo );
17440
17441 static void CFHostCmd( void )
17442 {
17443 OSStatus err;
17444 CFStringRef name;
17445 Boolean success;
17446 CFHostRef host = NULL;
17447 CFHostClientContext context;
17448 CFStreamError streamErr;
17449
17450 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
17451 require_action( name, exit, err = kUnknownErr );
17452
17453 host = CFHostCreateWithName( kCFAllocatorDefault, name );
17454 ForgetCF( &name );
17455 require_action( host, exit, err = kUnknownErr );
17456
17457 memset( &context, 0, sizeof( context ) );
17458 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
17459 require_action( success, exit, err = kUnknownErr );
17460
17461 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
17462
17463 // Print prologue.
17464
17465 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
17466 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17467 FPrintF( stdout, "---\n" );
17468
17469 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
17470 require_action( success, exit, err = kUnknownErr );
17471 err = kNoErr;
17472
17473 CFRunLoopRun();
17474
17475 exit:
17476 CFReleaseNullSafe( host );
17477 if( err ) exit( 1 );
17478 }
17479
17480 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
17481 {
17482 OSStatus err;
17483 struct timeval now;
17484
17485 gettimeofday( &now, NULL );
17486
17487 Unused( inInfoType );
17488 Unused( inInfo );
17489
17490 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
17491 {
17492 err = inError->error;
17493 if( inError->domain == kCFStreamErrorDomainNetDB )
17494 {
17495 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
17496 }
17497 else
17498 {
17499 FPrintF( stderr, "Error %#m\n", err );
17500 }
17501 }
17502 else
17503 {
17504 CFArrayRef addresses;
17505 CFIndex count, i;
17506 CFDataRef addrData;
17507 const struct sockaddr * sockAddr;
17508 Boolean wasResolved = false;
17509
17510 addresses = CFHostGetAddressing( inHost, &wasResolved );
17511 check( wasResolved );
17512
17513 if( addresses )
17514 {
17515 count = CFArrayGetCount( addresses );
17516 for( i = 0; i < count; ++i )
17517 {
17518 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
17519 require_noerr( err, exit );
17520
17521 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
17522 FPrintF( stdout, "%##a\n", sockAddr );
17523 }
17524 }
17525 err = kNoErr;
17526 }
17527
17528 FPrintF( stdout, "---\n" );
17529 FPrintF( stdout, "End time: %{du:time}\n", &now );
17530
17531 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
17532
17533 exit:
17534 exit( err ? 1 : 0 );
17535 }
17536
17537 //===========================================================================================================================
17538 // DNSConfigAddCmd
17539 //
17540 // Note: Based on ajn's supplemental test tool.
17541 //===========================================================================================================================
17542
17543 static void DNSConfigAddCmd( void )
17544 {
17545 OSStatus err;
17546 CFMutableDictionaryRef dict = NULL;
17547 CFMutableArrayRef array = NULL;
17548 size_t i;
17549 SCDynamicStoreRef store = NULL;
17550 CFStringRef key = NULL;
17551 Boolean success;
17552
17553 // Create dictionary.
17554
17555 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
17556 require_action( dict, exit, err = kNoMemoryErr );
17557
17558 // Add DNS server IP addresses.
17559
17560 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
17561 require_action( array, exit, err = kNoMemoryErr );
17562
17563 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
17564 {
17565 CFStringRef addrStr;
17566
17567 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
17568 require_action( addrStr, exit, err = kUnknownErr );
17569
17570 CFArrayAppendValue( array, addrStr );
17571 CFRelease( addrStr );
17572 }
17573
17574 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
17575 ForgetCF( &array );
17576
17577 // Add domains, if any.
17578
17579 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
17580 require_action( array, exit, err = kNoMemoryErr );
17581
17582 if( gDNSConfigAdd_DomainCount > 0 )
17583 {
17584 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
17585 {
17586 CFStringRef domainStr;
17587
17588 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
17589 require_action( domainStr, exit, err = kUnknownErr );
17590
17591 CFArrayAppendValue( array, domainStr );
17592 CFRelease( domainStr );
17593 }
17594 }
17595 else
17596 {
17597 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17598
17599 CFArrayAppendValue( array, CFSTR( "" ) );
17600 }
17601
17602 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
17603 ForgetCF( &array );
17604
17605 // Add interface, if any.
17606
17607 if( gDNSConfigAdd_Interface )
17608 {
17609 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
17610 require_noerr( err, exit );
17611
17612 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
17613 }
17614
17615 // Set dictionary in dynamic store.
17616
17617 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17618 err = map_scerror( store );
17619 require_noerr( err, exit );
17620
17621 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
17622 require_action( key, exit, err = kUnknownErr );
17623
17624 success = SCDynamicStoreSetValue( store, key, dict );
17625 require_action( success, exit, err = kUnknownErr );
17626
17627 exit:
17628 CFReleaseNullSafe( dict );
17629 CFReleaseNullSafe( array );
17630 CFReleaseNullSafe( store );
17631 CFReleaseNullSafe( key );
17632 gExitCode = err ? 1 : 0;
17633 }
17634
17635 //===========================================================================================================================
17636 // DNSConfigRemoveCmd
17637 //===========================================================================================================================
17638
17639 static void DNSConfigRemoveCmd( void )
17640 {
17641 OSStatus err;
17642 SCDynamicStoreRef store = NULL;
17643 CFStringRef key = NULL;
17644 Boolean success;
17645
17646 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17647 err = map_scerror( store );
17648 require_noerr( err, exit );
17649
17650 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
17651 require_action( key, exit, err = kUnknownErr );
17652
17653 success = SCDynamicStoreRemoveValue( store, key );
17654 require_action( success, exit, err = kUnknownErr );
17655
17656 exit:
17657 CFReleaseNullSafe( store );
17658 CFReleaseNullSafe( key );
17659 gExitCode = err ? 1 : 0;
17660 }
17661
17662 //===========================================================================================================================
17663 // XPCSendCmd
17664 //===========================================================================================================================
17665
17666 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
17667
17668 static void XPCSendCmd( void )
17669 {
17670 OSStatus err;
17671 xpc_object_t msg, reply;
17672
17673 err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
17674 require_noerr_quiet( err, exit );
17675
17676 FPrintF( stdout, "Service: %s\n", gXPCSend_ServiceName );
17677 FPrintF( stdout, "Message: %s\n", gXPCSend_MessageStr );
17678 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17679 FPrintF( stdout, "---\n" );
17680 FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
17681
17682 err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
17683 xpc_forget( &msg );
17684 require_noerr_quiet( err, exit );
17685
17686 FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
17687 FPrintF( stdout, "---\n" );
17688 FPrintF( stdout, "End time: %{du:time}\n", NULL );
17689 xpc_forget( &reply );
17690
17691 exit:
17692 if( err ) ErrQuit( 1, "error: %#m\n", err );
17693 }
17694
17695 //===========================================================================================================================
17696 // _XPCDictionaryCreateFromString
17697 //===========================================================================================================================
17698
17699 #define kXPCObjectPrefix_Bool "bool:"
17700 #define kXPCObjectPrefix_Data "data:"
17701 #define kXPCObjectPrefix_Int64 "int:"
17702 #define kXPCObjectPrefix_String "string:"
17703 #define kXPCObjectPrefix_UInt64 "uint:"
17704 #define kXPCObjectPrefix_UUID "uuid:"
17705
17706 typedef struct XPCListItem XPCListItem;
17707 struct XPCListItem
17708 {
17709 XPCListItem * next;
17710 xpc_object_t obj;
17711 char * key;
17712 };
17713
17714 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
17715 static void _XPCListItemFree( XPCListItem *inItem );
17716 static void _XPCListFree( XPCListItem *inList );
17717
17718 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject );
17719
17720 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
17721 {
17722 OSStatus err;
17723 xpc_object_t container;
17724 const char * ptr = inString;
17725 const char * const end = inString + strlen( inString );
17726 XPCListItem * list = NULL;
17727
17728 container = xpc_dictionary_create( NULL, NULL, 0 );
17729 require_action( container, exit, err = kNoMemoryErr );
17730
17731 while( *ptr )
17732 {
17733 xpc_type_t containerType;
17734 xpc_object_t value;
17735 int c;
17736 char keyStr[ 256 ];
17737 char valStr[ 256 ];
17738
17739 // At this point, zero or more of the current container's elements have been parsed.
17740 // Skip the white space leading up to the container's next element, if any, or the container's end.
17741
17742 while( isspace_safe( *ptr ) ) ++ptr;
17743
17744 // Check if we're done with the current container.
17745
17746 c = *ptr;
17747 if( c == '\0' ) break;
17748
17749 containerType = xpc_get_type( container );
17750 if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
17751 ( ( containerType == XPC_TYPE_ARRAY ) && ( c == ']' ) ) )
17752 {
17753 XPCListItem * item;
17754
17755 item = list;
17756 require_action_quiet( item, exit, err = kMalformedErr );
17757
17758 ++ptr;
17759
17760 // Add the current container to its parent container.
17761
17762 if( item->key )
17763 {
17764 xpc_dictionary_set_value( item->obj, item->key, container );
17765 }
17766 else
17767 {
17768 xpc_array_append_value( item->obj, container );
17769 }
17770
17771 // Continue with the parent container.
17772
17773 xpc_release( container );
17774 container = xpc_retain( item->obj );
17775 list = item->next;
17776 _XPCListItemFree( item );
17777 continue;
17778 }
17779
17780 // If the current container is a dictionary, parse the key string.
17781
17782 if( containerType == XPC_TYPE_DICTIONARY )
17783 {
17784 err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
17785 require_noerr_quiet( err, exit );
17786
17787 c = *ptr;
17788 require_action_quiet( c == '=', exit, err = kMalformedErr );
17789 ++ptr;
17790 }
17791
17792 // Check if the value is a dictionary ({...}) or an array ([...]).
17793
17794 c = *ptr;
17795 if( ( c == '{' ) || ( c == '[' ) )
17796 {
17797 XPCListItem * item;
17798
17799 ++ptr;
17800
17801 // Save the current container.
17802
17803 err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
17804 require_noerr( err, exit );
17805
17806 item->next = list;
17807 list = item;
17808 item = NULL;
17809
17810 // Create and continue with the child container.
17811
17812 xpc_release( container );
17813 if( c == '{' )
17814 {
17815 container = xpc_dictionary_create( NULL, NULL, 0 );
17816 require_action( container, exit, err = kNoMemoryErr );
17817 }
17818 else
17819 {
17820 container = xpc_array_create( NULL, 0 );
17821 require_action( container, exit, err = kNoMemoryErr );
17822 }
17823 continue;
17824 }
17825
17826 // Parse the value string.
17827
17828 err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
17829 require_noerr_quiet( err, exit );
17830
17831 err = _XPCObjectFromString( valStr, &value );
17832 require_noerr_quiet( err, exit );
17833
17834 if( containerType == XPC_TYPE_DICTIONARY )
17835 {
17836 xpc_dictionary_set_value( container, keyStr, value );
17837 }
17838 else
17839 {
17840 xpc_array_append_value( container, value );
17841 }
17842 xpc_forget( &value );
17843 }
17844 require_action_quiet( !list, exit, err = kMalformedErr );
17845
17846 check( container );
17847 check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
17848
17849 *outDict = container;
17850 container = NULL;
17851 err = kNoErr;
17852
17853 exit:
17854 xpc_release_null_safe( container );
17855 if( list ) _XPCListFree( list );
17856 return( err );
17857 }
17858
17859 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject )
17860 {
17861 OSStatus err;
17862 xpc_object_t object;
17863
17864 if( 0 ) {}
17865
17866 // Bool
17867
17868 else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
17869 {
17870 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Bool );
17871 bool value;
17872
17873 if( IsTrueString( str, kSizeCString ) )
17874 {
17875 value = true;
17876 }
17877 else if( IsFalseString( str, kSizeCString ) )
17878 {
17879 value = false;
17880 }
17881 else
17882 {
17883 err = kValueErr;
17884 goto exit;
17885 }
17886
17887 object = xpc_bool_create( value );
17888 require_action( object, exit, err = kNoMemoryErr );
17889 }
17890
17891 // Data
17892
17893 else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
17894 {
17895 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Data );
17896 uint8_t * dataPtr;
17897 size_t dataLen;
17898
17899 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
17900 require_noerr( err, exit );
17901
17902 object = xpc_data_create( dataPtr, dataLen );
17903 free( dataPtr );
17904 require_action( object, exit, err = kNoMemoryErr );
17905 }
17906
17907 // Int64
17908
17909 else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
17910 {
17911 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
17912 int64_t i64;
17913
17914 i64 = _StringToInt64( str, &err );
17915 require_noerr_quiet( err, exit );
17916
17917 object = xpc_int64_create( i64 );
17918 require_action( object, exit, err = kNoMemoryErr );
17919 }
17920
17921 // String
17922
17923 else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
17924 {
17925 const char * const str = inString + sizeof_string( kXPCObjectPrefix_String );
17926
17927 object = xpc_string_create( str );
17928 require_action( object, exit, err = kNoMemoryErr );
17929 }
17930
17931 // UInt64
17932
17933 else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
17934 {
17935 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
17936 uint64_t u64;
17937
17938 u64 = _StringToUInt64( str, &err );
17939 require_noerr_quiet( err, exit );
17940
17941 object = xpc_uint64_create( u64 );
17942 require_action( object, exit, err = kNoMemoryErr );
17943 }
17944
17945 // UUID
17946
17947 else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
17948 {
17949 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UUID );
17950 uuid_t uuid;
17951
17952 err = uuid_parse( str, uuid );
17953 require_noerr_action_quiet( err, exit, err = kValueErr );
17954
17955 object = xpc_uuid_create( uuid );
17956 require_action( object, exit, err = kNoMemoryErr );
17957 }
17958
17959 // Unsupported prefix
17960
17961 else
17962 {
17963 err = kValueErr;
17964 goto exit;
17965 }
17966
17967 *outObject = object;
17968 err = kNoErr;
17969
17970 exit:
17971 return( err );
17972 }
17973
17974 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
17975 {
17976 OSStatus err;
17977 XPCListItem * item;
17978
17979 item = (XPCListItem *) calloc( 1, sizeof( *item ) );
17980 require_action( item, exit, err = kNoMemoryErr );
17981
17982 item->obj = xpc_retain( inObject );
17983 if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
17984 {
17985 item->key = strdup( inKey );
17986 require_action( item->key, exit, err = kNoMemoryErr );
17987 }
17988
17989 *outItem = item;
17990 item = NULL;
17991 err = kNoErr;
17992
17993 exit:
17994 if( item ) _XPCListItemFree( item );
17995 return( err );
17996 }
17997
17998 static void _XPCListItemFree( XPCListItem *inItem )
17999 {
18000 xpc_forget( &inItem->obj );
18001 ForgetMem( &inItem->key );
18002 free( inItem );
18003 }
18004
18005 static void _XPCListFree( XPCListItem *inList )
18006 {
18007 XPCListItem * item;
18008
18009 while( ( item = inList ) != NULL )
18010 {
18011 inList = item->next;
18012 _XPCListItemFree( item );
18013 }
18014 }
18015 #endif // TARGET_OS_DARWIN
18016
18017 #if( MDNSRESPONDER_PROJECT )
18018 //===========================================================================================================================
18019 // InterfaceMonitorCmd
18020 //===========================================================================================================================
18021
18022 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
18023 static void _InterfaceMonitorSignalHandler( void *inContext );
18024
18025 static void InterfaceMonitorCmd( void )
18026 {
18027 OSStatus err;
18028 mdns_interface_monitor_t monitor;
18029 dispatch_source_t signalSource = NULL;
18030 uint32_t ifIndex;
18031 __block int exitCode;
18032
18033 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
18034 require_noerr_quiet( err, exit );
18035
18036 monitor = mdns_interface_monitor_create( ifIndex );
18037 require_action( monitor, exit, err = kNoResourcesErr );
18038
18039 exitCode = 0;
18040 mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
18041 mdns_interface_monitor_set_event_handler( monitor,
18042 ^( mdns_event_t inEvent, OSStatus inError )
18043 {
18044 switch( inEvent )
18045 {
18046 case mdns_event_error:
18047 FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
18048 mdns_interface_monitor_invalidate( monitor );
18049 exitCode = 1;
18050 break;
18051
18052 case mdns_event_invalidated:
18053 FPrintF( stdout, "Interface monitor invalidated.\n" );
18054 mdns_release( monitor );
18055 exit( exitCode );
18056
18057 default:
18058 FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
18059 break;
18060 }
18061 } );
18062 mdns_interface_monitor_set_update_handler( monitor,
18063 ^( __unused mdns_interface_flags_t inChangeFlags )
18064 {
18065 _InterfaceMonitorPrint( monitor );
18066 } );
18067
18068 _InterfaceMonitorPrint( monitor );
18069 mdns_interface_monitor_activate( monitor );
18070
18071 signal( SIGINT, SIG_IGN );
18072 err = DispatchSignalSourceCreate( SIGINT, _InterfaceMonitorSignalHandler, monitor, &signalSource );
18073 require_noerr( err, exit );
18074 dispatch_resume( signalSource );
18075
18076 dispatch_main();
18077
18078 exit:
18079 if( err ) ErrQuit( 1, "error: %#m\n", err );
18080 }
18081
18082 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
18083 {
18084 FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
18085 }
18086
18087 static void _InterfaceMonitorSignalHandler( void *inContext )
18088 {
18089 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
18090 }
18091
18092 //===========================================================================================================================
18093 // DNSProxyCmd
18094 //===========================================================================================================================
18095
18096 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
18097 static void _DNSProxyCmdSignalHandler( void *inContext );
18098
18099 static void DNSProxyCmd( void )
18100 {
18101 OSStatus err;
18102 size_t i;
18103 DNSXConnRef connection;
18104 IfIndex inputIfIndexes[ MaxInputIf ];
18105 dispatch_source_t sigIntSource = NULL;
18106 dispatch_source_t sigTermSource = NULL;
18107 uint32_t outputIfIndex;
18108 char ifName[ kInterfaceNameBufLen ];
18109
18110 if( gDNSProxy_InputInterfaceCount > MaxInputIf )
18111 {
18112 FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
18113 gDNSProxy_InputInterfaceCount, MaxInputIf );
18114 err = kRangeErr;
18115 goto exit;
18116 }
18117
18118 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18119 {
18120 uint32_t ifIndex;
18121
18122 err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
18123 require_noerr_quiet( err, exit );
18124
18125 inputIfIndexes[ i ] = ifIndex;
18126 }
18127 while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0; // Remaining interface indexes are required to be 0.
18128
18129 if( gDNSProxy_OutputInterface )
18130 {
18131 err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
18132 require_noerr_quiet( err, exit );
18133 }
18134 else
18135 {
18136 outputIfIndex = kDNSIfindexAny;
18137 }
18138
18139 FPrintF( stdout, "Input Interfaces:" );
18140 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18141 {
18142 const uint32_t ifIndex = (uint32_t) inputIfIndexes[ i ];
18143
18144 FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
18145 }
18146 FPrintF( stdout, "\n" );
18147 FPrintF( stdout, "Output Interface: %u (%s)\n", outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
18148 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18149 FPrintF( stdout, "---\n" );
18150
18151 connection = NULL;
18152 err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex, dispatch_get_main_queue(),
18153 _DNSProxyCallback );
18154 require_noerr_quiet( err, exit );
18155
18156 signal( SIGINT, SIG_IGN );
18157 err = DispatchSignalSourceCreate( SIGINT, _DNSProxyCmdSignalHandler, connection, &sigIntSource );
18158 require_noerr( err, exit );
18159 dispatch_activate( sigIntSource );
18160
18161 signal( SIGTERM, SIG_IGN );
18162 err = DispatchSignalSourceCreate( SIGTERM, _DNSProxyCmdSignalHandler, connection, &sigTermSource );
18163 require_noerr( err, exit );
18164 dispatch_activate( sigTermSource );
18165
18166 dispatch_main();
18167
18168 exit:
18169 if( err ) ErrQuit( 1, "error: %#m\n", err );
18170 }
18171
18172 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
18173 {
18174 Unused( inConnection );
18175
18176 if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
18177 }
18178
18179 static void _DNSProxyCmdSignalHandler( void *inContext )
18180 {
18181 DNSXConnRef const connection = (DNSXConnRef) inContext;
18182 struct timeval now;
18183
18184 gettimeofday( &now, NULL );
18185
18186 DNSXRefDeAlloc( connection );
18187
18188 FPrintF( stdout, "---\n" );
18189 FPrintF( stdout, "End time: %{du:time}\n", &now );
18190 exit( 0 );
18191 }
18192
18193 #endif // MDNSRESPONDER_PROJECT
18194
18195 //===========================================================================================================================
18196 // DaemonVersionCmd
18197 //===========================================================================================================================
18198
18199 static void DaemonVersionCmd( void )
18200 {
18201 OSStatus err;
18202 uint32_t size, version;
18203 char strBuf[ 16 ];
18204
18205 size = (uint32_t) sizeof( version );
18206 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
18207 require_noerr( err, exit );
18208
18209 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
18210
18211 exit:
18212 if( err ) exit( 1 );
18213 }
18214
18215 //===========================================================================================================================
18216 // Exit
18217 //===========================================================================================================================
18218
18219 static void Exit( void *inContext )
18220 {
18221 const char * const reason = (const char *) inContext;
18222
18223 FPrintF( stdout, "---\n" );
18224 FPrintF( stdout, "End time: %{du:time}\n", NULL );
18225 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
18226 exit( gExitCode );
18227 }
18228
18229 //===========================================================================================================================
18230 // _PrintFExtensionTimestampHandler
18231 //===========================================================================================================================
18232
18233 static int
18234 _PrintFExtensionTimestampHandler(
18235 PrintFContext * inContext,
18236 PrintFFormat * inFormat,
18237 PrintFVAList * inArgs,
18238 void * inUserContext )
18239 {
18240 struct timeval now;
18241 const struct timeval * tv;
18242 struct tm * localTime;
18243 size_t len;
18244 int n;
18245 char dateTimeStr[ 32 ];
18246
18247 Unused( inUserContext );
18248
18249 tv = va_arg( inArgs->args, const struct timeval * );
18250 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18251
18252 if( !tv )
18253 {
18254 gettimeofday( &now, NULL );
18255 tv = &now;
18256 }
18257 localTime = localtime( &tv->tv_sec );
18258 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
18259 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
18260
18261 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
18262
18263 exit:
18264 return( n );
18265 }
18266
18267 //===========================================================================================================================
18268 // _PrintFExtensionDNSMessageHandler
18269 //===========================================================================================================================
18270
18271 static int
18272 _PrintFExtensionDNSMessageHandler(
18273 PrintFContext * inContext,
18274 PrintFFormat * inFormat,
18275 PrintFVAList * inArgs,
18276 void * inUserContext )
18277 {
18278 OSStatus err;
18279 const void * msgPtr;
18280 size_t msgLen;
18281 char * text;
18282 int n;
18283 Boolean isMDNS;
18284 Boolean printRawRData;
18285
18286 Unused( inUserContext );
18287
18288 msgPtr = va_arg( inArgs->args, const void * );
18289 msgLen = va_arg( inArgs->args, size_t );
18290 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18291
18292 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
18293 printRawRData = ( inFormat->precision > 0 ) ? true : false;
18294 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
18295 if( !err )
18296 {
18297 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
18298 free( text );
18299 }
18300 else
18301 {
18302 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
18303 }
18304
18305 exit:
18306 return( n );
18307 }
18308
18309 //===========================================================================================================================
18310 // _PrintFExtensionCallbackFlagsHandler
18311 //===========================================================================================================================
18312
18313 static int
18314 _PrintFExtensionCallbackFlagsHandler(
18315 PrintFContext * inContext,
18316 PrintFFormat * inFormat,
18317 PrintFVAList * inArgs,
18318 void * inUserContext )
18319 {
18320 DNSServiceFlags flags;
18321 int n;
18322
18323 Unused( inUserContext );
18324
18325 flags = va_arg( inArgs->args, DNSServiceFlags );
18326 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18327
18328 n = PrintFCore( inContext, "%08X %s%c %c%c",
18329 flags, DNSServiceFlagsToAddRmvStr( flags ),
18330 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
18331 ( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
18332 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '*' : ' ' );
18333
18334 exit:
18335 return( n );
18336 }
18337
18338 //===========================================================================================================================
18339 // _PrintFExtensionDNSRecordDataHandler
18340 //===========================================================================================================================
18341
18342 static int
18343 _PrintFExtensionDNSRecordDataHandler(
18344 PrintFContext * inContext,
18345 PrintFFormat * inFormat,
18346 PrintFVAList * inArgs,
18347 void * inUserContext )
18348 {
18349 const void * rdataPtr;
18350 unsigned int rdataLen, rdataType;
18351 int n, fieldWidth;
18352
18353 Unused( inUserContext );
18354
18355 rdataType = va_arg( inArgs->args, unsigned int );
18356 rdataPtr = va_arg( inArgs->args, const void * );
18357 rdataLen = va_arg( inArgs->args, unsigned int );
18358 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18359
18360 check( inFormat->fieldWidth < INT_MAX );
18361 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
18362
18363 if( rdataLen > 0 )
18364 {
18365 char * rdataStr = NULL;
18366
18367 DNSRecordDataToString( rdataPtr, rdataLen, rdataType, NULL, 0, &rdataStr );
18368 if( rdataStr )
18369 {
18370 n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
18371 free( rdataStr );
18372 }
18373 else
18374 {
18375 n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
18376 }
18377 }
18378 else
18379 {
18380 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
18381 }
18382
18383 exit:
18384 return( n );
18385 }
18386
18387 //===========================================================================================================================
18388 // GetDNSSDFlagsFromOpts
18389 //===========================================================================================================================
18390
18391 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
18392 {
18393 DNSServiceFlags flags;
18394
18395 flags = (DNSServiceFlags) gDNSSDFlags;
18396 if( flags & kDNSServiceFlagsShareConnection )
18397 {
18398 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18399 kDNSServiceFlagsShareConnection );
18400 }
18401
18402 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
18403 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
18404 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
18405 if( gDNSSDFlag_DenyConstrained ) flags |= kDNSServiceFlagsDenyConstrained;
18406 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
18407 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
18408 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
18409 if( gDNSSDFlag_KnownUnique ) flags |= kDNSServiceFlagsKnownUnique;
18410 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
18411 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
18412 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
18413 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
18414 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
18415 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
18416 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
18417 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
18418 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
18419 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
18420
18421 return( flags );
18422 }
18423
18424 //===========================================================================================================================
18425 // CreateConnectionFromArgString
18426 //===========================================================================================================================
18427
18428 static OSStatus
18429 CreateConnectionFromArgString(
18430 const char * inString,
18431 dispatch_queue_t inQueue,
18432 DNSServiceRef * outSDRef,
18433 ConnectionDesc * outDesc )
18434 {
18435 OSStatus err;
18436 DNSServiceRef sdRef = NULL;
18437 ConnectionType type;
18438 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
18439 uint8_t uuid[ 16 ];
18440
18441 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
18442 {
18443 err = DNSServiceCreateConnection( &sdRef );
18444 require_noerr( err, exit );
18445 type = kConnectionType_Normal;
18446 }
18447 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
18448 {
18449 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
18450
18451 err = StringToInt32( pidStr, &pid );
18452 if( err )
18453 {
18454 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
18455 err = kParamErr;
18456 goto exit;
18457 }
18458
18459 memset( uuid, 0, sizeof( uuid ) );
18460 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
18461 if( err )
18462 {
18463 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
18464 goto exit;
18465 }
18466 type = kConnectionType_DelegatePID;
18467 }
18468 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
18469 {
18470 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
18471
18472 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
18473
18474 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
18475 if( err )
18476 {
18477 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
18478 err = kParamErr;
18479 goto exit;
18480 }
18481
18482 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
18483 if( err )
18484 {
18485 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
18486 goto exit;
18487 }
18488 type = kConnectionType_DelegateUUID;
18489 }
18490 else
18491 {
18492 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
18493 err = kParamErr;
18494 goto exit;
18495 }
18496
18497 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
18498 require_noerr( err, exit );
18499
18500 *outSDRef = sdRef;
18501 if( outDesc )
18502 {
18503 outDesc->type = type;
18504 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
18505 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
18506 }
18507 sdRef = NULL;
18508
18509 exit:
18510 if( sdRef ) DNSServiceRefDeallocate( sdRef );
18511 return( err );
18512 }
18513
18514 //===========================================================================================================================
18515 // InterfaceIndexFromArgString
18516 //===========================================================================================================================
18517
18518 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
18519 {
18520 OSStatus err;
18521 uint32_t ifIndex;
18522
18523 if( inString )
18524 {
18525 ifIndex = if_nametoindex( inString );
18526 if( ifIndex == 0 )
18527 {
18528 err = StringToUInt32( inString, &ifIndex );
18529 if( err )
18530 {
18531 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
18532 err = kParamErr;
18533 goto exit;
18534 }
18535 }
18536 }
18537 else
18538 {
18539 ifIndex = 0;
18540 }
18541
18542 *outIndex = ifIndex;
18543 err = kNoErr;
18544
18545 exit:
18546 return( err );
18547 }
18548
18549 //===========================================================================================================================
18550 // RecordDataFromArgString
18551 //===========================================================================================================================
18552
18553 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
18554 {
18555 OSStatus err;
18556 uint8_t * dataPtr = NULL;
18557 size_t dataLen;
18558
18559 if( 0 ) {}
18560
18561 // Domain name
18562
18563 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
18564 {
18565 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
18566
18567 err = StringToDomainName( str, &dataPtr, &dataLen );
18568 require_noerr_quiet( err, exit );
18569 }
18570
18571 // File path
18572
18573 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
18574 {
18575 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
18576
18577 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
18578 require_noerr( err, exit );
18579 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18580 }
18581
18582 // Hexadecimal string
18583
18584 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
18585 {
18586 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
18587
18588 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
18589 require_noerr( err, exit );
18590 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18591 }
18592
18593 // IPv4 address string
18594
18595 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
18596 {
18597 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
18598
18599 err = StringToARecordData( str, &dataPtr, &dataLen );
18600 require_noerr_quiet( err, exit );
18601 }
18602
18603 // IPv6 address string
18604
18605 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
18606 {
18607 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
18608
18609 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
18610 require_noerr_quiet( err, exit );
18611 }
18612
18613 // SRV record
18614
18615 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
18616 {
18617 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
18618
18619 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
18620 require_noerr( err, exit );
18621 }
18622
18623 // String with escaped hex and octal bytes
18624
18625 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
18626 {
18627 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
18628 const char * const end = str + strlen( str );
18629 size_t copiedLen;
18630 size_t totalLen;
18631 Boolean success;
18632
18633 if( str < end )
18634 {
18635 success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
18636 require_action( success, exit, err = kParamErr );
18637 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18638
18639 dataLen = totalLen;
18640 dataPtr = (uint8_t *) malloc( dataLen );
18641 require_action( dataPtr, exit, err = kNoMemoryErr );
18642
18643 success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
18644 require_action( success, exit, err = kParamErr );
18645 check( copiedLen == dataLen );
18646 }
18647 else
18648 {
18649 dataPtr = NULL;
18650 dataLen = 0;
18651 }
18652 }
18653
18654 // TXT record
18655
18656 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
18657 {
18658 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
18659
18660 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
18661 require_noerr( err, exit );
18662 }
18663
18664 // Unrecognized format
18665
18666 else
18667 {
18668 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
18669 err = kParamErr;
18670 goto exit;
18671 }
18672
18673 err = kNoErr;
18674 *outDataLen = dataLen;
18675 *outDataPtr = dataPtr;
18676 dataPtr = NULL;
18677
18678 exit:
18679 FreeNullSafe( dataPtr );
18680 return( err );
18681 }
18682
18683 //===========================================================================================================================
18684 // RecordTypeFromArgString
18685 //===========================================================================================================================
18686
18687 typedef struct
18688 {
18689 uint16_t value; // Record type's numeric value.
18690 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18691
18692 } RecordType;
18693
18694 static const RecordType kRecordTypes[] =
18695 {
18696 // Common types.
18697
18698 { kDNSServiceType_A, "A" },
18699 { kDNSServiceType_AAAA, "AAAA" },
18700 { kDNSServiceType_PTR, "PTR" },
18701 { kDNSServiceType_SRV, "SRV" },
18702 { kDNSServiceType_TXT, "TXT" },
18703 { kDNSServiceType_CNAME, "CNAME" },
18704 { kDNSServiceType_SOA, "SOA" },
18705 { kDNSServiceType_NSEC, "NSEC" },
18706 { kDNSServiceType_NS, "NS" },
18707 { kDNSServiceType_MX, "MX" },
18708 { kDNSServiceType_ANY, "ANY" },
18709 { kDNSServiceType_OPT, "OPT" },
18710
18711 // Less common types.
18712
18713 { kDNSServiceType_MD, "MD" },
18714 { kDNSServiceType_NS, "NS" },
18715 { kDNSServiceType_MD, "MD" },
18716 { kDNSServiceType_MF, "MF" },
18717 { kDNSServiceType_MB, "MB" },
18718 { kDNSServiceType_MG, "MG" },
18719 { kDNSServiceType_MR, "MR" },
18720 { kDNSServiceType_NULL, "NULL" },
18721 { kDNSServiceType_WKS, "WKS" },
18722 { kDNSServiceType_HINFO, "HINFO" },
18723 { kDNSServiceType_MINFO, "MINFO" },
18724 { kDNSServiceType_RP, "RP" },
18725 { kDNSServiceType_AFSDB, "AFSDB" },
18726 { kDNSServiceType_X25, "X25" },
18727 { kDNSServiceType_ISDN, "ISDN" },
18728 { kDNSServiceType_RT, "RT" },
18729 { kDNSServiceType_NSAP, "NSAP" },
18730 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
18731 { kDNSServiceType_SIG, "SIG" },
18732 { kDNSServiceType_KEY, "KEY" },
18733 { kDNSServiceType_PX, "PX" },
18734 { kDNSServiceType_GPOS, "GPOS" },
18735 { kDNSServiceType_LOC, "LOC" },
18736 { kDNSServiceType_NXT, "NXT" },
18737 { kDNSServiceType_EID, "EID" },
18738 { kDNSServiceType_NIMLOC, "NIMLOC" },
18739 { kDNSServiceType_ATMA, "ATMA" },
18740 { kDNSServiceType_NAPTR, "NAPTR" },
18741 { kDNSServiceType_KX, "KX" },
18742 { kDNSServiceType_CERT, "CERT" },
18743 { kDNSServiceType_A6, "A6" },
18744 { kDNSServiceType_DNAME, "DNAME" },
18745 { kDNSServiceType_SINK, "SINK" },
18746 { kDNSServiceType_APL, "APL" },
18747 { kDNSServiceType_DS, "DS" },
18748 { kDNSServiceType_SSHFP, "SSHFP" },
18749 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
18750 { kDNSServiceType_RRSIG, "RRSIG" },
18751 { kDNSServiceType_DNSKEY, "DNSKEY" },
18752 { kDNSServiceType_DHCID, "DHCID" },
18753 { kDNSServiceType_NSEC3, "NSEC3" },
18754 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
18755 { kDNSServiceType_HIP, "HIP" },
18756 { kDNSServiceType_SPF, "SPF" },
18757 { kDNSServiceType_UINFO, "UINFO" },
18758 { kDNSServiceType_UID, "UID" },
18759 { kDNSServiceType_GID, "GID" },
18760 { kDNSServiceType_UNSPEC, "UNSPEC" },
18761 { kDNSServiceType_TKEY, "TKEY" },
18762 { kDNSServiceType_TSIG, "TSIG" },
18763 { kDNSServiceType_IXFR, "IXFR" },
18764 { kDNSServiceType_AXFR, "AXFR" },
18765 { kDNSServiceType_MAILB, "MAILB" },
18766 { kDNSServiceType_MAILA, "MAILA" }
18767 };
18768
18769 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
18770 {
18771 OSStatus err;
18772 int32_t i32;
18773 const RecordType * type;
18774 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18775
18776 for( type = kRecordTypes; type < end; ++type )
18777 {
18778 if( strcasecmp( type->name, inString ) == 0 )
18779 {
18780 *outValue = type->value;
18781 return( kNoErr );
18782 }
18783 }
18784
18785 err = StringToInt32( inString, &i32 );
18786 require_noerr_quiet( err, exit );
18787 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18788
18789 *outValue = (uint16_t) i32;
18790
18791 exit:
18792 return( err );
18793 }
18794
18795 //===========================================================================================================================
18796 // RecordClassFromArgString
18797 //===========================================================================================================================
18798
18799 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
18800 {
18801 OSStatus err;
18802 int32_t i32;
18803
18804 if( strcasecmp( inString, "IN" ) == 0 )
18805 {
18806 *outValue = kDNSServiceClass_IN;
18807 err = kNoErr;
18808 goto exit;
18809 }
18810
18811 err = StringToInt32( inString, &i32 );
18812 require_noerr_quiet( err, exit );
18813 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18814
18815 *outValue = (uint16_t) i32;
18816
18817 exit:
18818 return( err );
18819 }
18820
18821 //===========================================================================================================================
18822 // InterfaceIndexToName
18823 //===========================================================================================================================
18824
18825 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
18826 {
18827 switch( inIfIndex )
18828 {
18829 case kDNSServiceInterfaceIndexAny:
18830 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
18831 break;
18832
18833 case kDNSServiceInterfaceIndexLocalOnly:
18834 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
18835 break;
18836
18837 case kDNSServiceInterfaceIndexUnicast:
18838 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
18839 break;
18840
18841 case kDNSServiceInterfaceIndexP2P:
18842 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
18843 break;
18844
18845 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18846 case kDNSServiceInterfaceIndexBLE:
18847 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
18848 break;
18849 #endif
18850
18851 default:
18852 {
18853 const char * name;
18854
18855 name = if_indextoname( inIfIndex, inNameBuf );
18856 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
18857 break;
18858 }
18859 }
18860
18861 return( inNameBuf );
18862 }
18863
18864 //===========================================================================================================================
18865 // RecordTypeToString
18866 //===========================================================================================================================
18867
18868 static const char * RecordTypeToString( unsigned int inValue )
18869 {
18870 const RecordType * type;
18871 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18872
18873 for( type = kRecordTypes; type < end; ++type )
18874 {
18875 if( type->value == inValue ) return( type->name );
18876 }
18877 return( "???" );
18878 }
18879
18880 //===========================================================================================================================
18881 // DNSRecordDataToString
18882 //===========================================================================================================================
18883
18884 static OSStatus
18885 DNSRecordDataToString(
18886 const void * inRDataPtr,
18887 size_t inRDataLen,
18888 unsigned int inRDataType,
18889 const void * inMsgPtr,
18890 size_t inMsgLen,
18891 char ** outString )
18892 {
18893 OSStatus err;
18894 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
18895 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
18896 char * rdataStr;
18897 const uint8_t * ptr;
18898 int n;
18899 char domainNameStr[ kDNSServiceMaxDomainName ];
18900
18901 rdataStr = NULL;
18902
18903 // A Record
18904
18905 if( inRDataType == kDNSServiceType_A )
18906 {
18907 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
18908
18909 ASPrintF( &rdataStr, "%.4a", rdataPtr );
18910 require_action( rdataStr, exit, err = kNoMemoryErr );
18911 }
18912
18913 // AAAA Record
18914
18915 else if( inRDataType == kDNSServiceType_AAAA )
18916 {
18917 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
18918
18919 ASPrintF( &rdataStr, "%.16a", rdataPtr );
18920 require_action( rdataStr, exit, err = kNoMemoryErr );
18921 }
18922
18923 // PTR, CNAME, or NS Record
18924
18925 else if( ( inRDataType == kDNSServiceType_PTR ) ||
18926 ( inRDataType == kDNSServiceType_CNAME ) ||
18927 ( inRDataType == kDNSServiceType_NS ) )
18928 {
18929 if( inMsgPtr )
18930 {
18931 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
18932 require_noerr( err, exit );
18933 }
18934 else
18935 {
18936 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
18937 require_noerr( err, exit );
18938 }
18939
18940 rdataStr = strdup( domainNameStr );
18941 require_action( rdataStr, exit, err = kNoMemoryErr );
18942 }
18943
18944 // SRV Record
18945
18946 else if( inRDataType == kDNSServiceType_SRV )
18947 {
18948 const dns_fixed_fields_srv * fields;
18949 const uint8_t * target;
18950 unsigned int priority, weight, port;
18951
18952 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
18953
18954 fields = (const dns_fixed_fields_srv *) rdataPtr;
18955 priority = dns_fixed_fields_srv_get_priority( fields );
18956 weight = dns_fixed_fields_srv_get_weight( fields );
18957 port = dns_fixed_fields_srv_get_port( fields );
18958 target = (const uint8_t *) &fields[ 1 ];
18959
18960 if( inMsgPtr )
18961 {
18962 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
18963 require_noerr( err, exit );
18964 }
18965 else
18966 {
18967 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
18968 require_noerr( err, exit );
18969 }
18970
18971 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
18972 require_action( rdataStr, exit, err = kNoMemoryErr );
18973 }
18974
18975 // TXT Record
18976
18977 else if( inRDataType == kDNSServiceType_TXT )
18978 {
18979 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
18980
18981 if( inRDataLen == 1 )
18982 {
18983 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
18984 require_action( rdataStr, exit, err = kNoMemoryErr );
18985 }
18986 else
18987 {
18988 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
18989 require_action( rdataStr, exit, err = kNoMemoryErr );
18990 }
18991 }
18992
18993 // SOA Record
18994
18995 else if( inRDataType == kDNSServiceType_SOA )
18996 {
18997 const dns_fixed_fields_soa * fields;
18998 uint32_t serial, refresh, retry, expire, minimum;
18999
19000 if( inMsgPtr )
19001 {
19002 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19003 require_noerr( err, exit );
19004
19005 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19006
19007 rdataStr = strdup( domainNameStr );
19008 require_action( rdataStr, exit, err = kNoMemoryErr );
19009
19010 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
19011 require_noerr( err, exit );
19012 }
19013 else
19014 {
19015 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19016 require_noerr( err, exit );
19017
19018 rdataStr = strdup( domainNameStr );
19019 require_action( rdataStr, exit, err = kNoMemoryErr );
19020
19021 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
19022 require_noerr( err, exit );
19023 }
19024
19025 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
19026
19027 fields = (const dns_fixed_fields_soa *) ptr;
19028 serial = dns_fixed_fields_soa_get_serial( fields );
19029 refresh = dns_fixed_fields_soa_get_refresh( fields );
19030 retry = dns_fixed_fields_soa_get_retry( fields );
19031 expire = dns_fixed_fields_soa_get_expire( fields );
19032 minimum = dns_fixed_fields_soa_get_minimum( fields );
19033
19034 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
19035 require_action( n > 0, exit, err = kUnknownErr );
19036 }
19037
19038 // NSEC Record
19039
19040 else if( inRDataType == kDNSServiceType_NSEC )
19041 {
19042 unsigned int windowBlock, bitmapLen, i, recordType;
19043 const uint8_t * bitmapPtr;
19044
19045 if( inMsgPtr )
19046 {
19047 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19048 require_noerr( err, exit );
19049 }
19050 else
19051 {
19052 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19053 require_noerr( err, exit );
19054 }
19055
19056 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19057
19058 rdataStr = strdup( domainNameStr );
19059 require_action( rdataStr, exit, err = kNoMemoryErr );
19060
19061 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
19062 {
19063 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19064
19065 windowBlock = ptr[ 0 ];
19066 bitmapLen = ptr[ 1 ];
19067 bitmapPtr = &ptr[ 2 ];
19068
19069 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
19070 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
19071
19072 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
19073 {
19074 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
19075 {
19076 recordType = ( windowBlock * 256 ) + i;
19077 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
19078 require_action( n > 0, exit, err = kUnknownErr );
19079 }
19080 }
19081 }
19082 }
19083
19084 // MX Record
19085
19086 else if( inRDataType == kDNSServiceType_MX )
19087 {
19088 uint16_t preference;
19089 const uint8_t * exchange;
19090
19091 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19092
19093 preference = ReadBig16( rdataPtr );
19094 exchange = &rdataPtr[ 2 ];
19095
19096 if( inMsgPtr )
19097 {
19098 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
19099 require_noerr( err, exit );
19100 }
19101 else
19102 {
19103 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
19104 require_noerr( err, exit );
19105 }
19106
19107 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
19108 require_action( n > 0, exit, err = kUnknownErr );
19109 }
19110
19111 // Unhandled record type
19112
19113 else
19114 {
19115 err = kNotHandledErr;
19116 goto exit;
19117 }
19118
19119 check( rdataStr );
19120 *outString = rdataStr;
19121 rdataStr = NULL;
19122 err = kNoErr;
19123
19124 exit:
19125 FreeNullSafe( rdataStr );
19126 return( err );
19127 }
19128
19129 //===========================================================================================================================
19130 // DNSMessageToText
19131 //===========================================================================================================================
19132
19133 #define DNSFlagsOpCodeToString( X ) ( \
19134 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19135 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19136 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19137 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19138 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19139 "Unassigned" )
19140
19141 #define DNSFlagsRCodeToString( X ) ( \
19142 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19143 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19144 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19145 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19146 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19147 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19148 "???" )
19149
19150 static OSStatus
19151 DNSMessageToText(
19152 const uint8_t * inMsgPtr,
19153 size_t inMsgLen,
19154 const Boolean inMDNS,
19155 const Boolean inPrintRaw,
19156 char ** outText )
19157 {
19158 OSStatus err;
19159 DataBuffer dataBuf;
19160 size_t len;
19161 const DNSHeader * hdr;
19162 const uint8_t * ptr;
19163 unsigned int id, flags, opcode, rcode;
19164 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
19165 uint8_t name[ kDomainNameLengthMax ];
19166 char nameStr[ kDNSServiceMaxDomainName ];
19167
19168 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
19169 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19170
19171 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
19172
19173 hdr = (DNSHeader *) inMsgPtr;
19174 id = DNSHeaderGetID( hdr );
19175 flags = DNSHeaderGetFlags( hdr );
19176 questionCount = DNSHeaderGetQuestionCount( hdr );
19177 answerCount = DNSHeaderGetAnswerCount( hdr );
19178 authorityCount = DNSHeaderGetAuthorityCount( hdr );
19179 additionalCount = DNSHeaderGetAdditionalCount( hdr );
19180 opcode = DNSFlagsGetOpCode( flags );
19181 rcode = DNSFlagsGetRCode( flags );
19182
19183 _Append( "ID: 0x%04X (%u)\n", id, id );
19184 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19185 flags,
19186 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
19187 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
19188 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
19189 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
19190 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
19191 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
19192 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
19193 DNSFlagsRCodeToString( rcode ) );
19194 _Append( "Question count: %u\n", questionCount );
19195 _Append( "Answer count: %u\n", answerCount );
19196 _Append( "Authority count: %u\n", authorityCount );
19197 _Append( "Additional count: %u\n", additionalCount );
19198
19199 ptr = (const uint8_t *) &hdr[ 1 ];
19200 for( i = 0; i < questionCount; ++i )
19201 {
19202 uint16_t qtype, qclass;
19203 Boolean isQU;
19204
19205 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
19206 require_noerr( err, exit );
19207
19208 err = DomainNameToString( name, NULL, nameStr, NULL );
19209 require_noerr( err, exit );
19210
19211 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
19212 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
19213
19214 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
19215
19216 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19217 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
19218 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
19219 }
19220
19221 totalRRCount = answerCount + authorityCount + additionalCount;
19222 for( i = 0; i < totalRRCount; ++i )
19223 {
19224 uint16_t type;
19225 uint16_t class;
19226 uint32_t ttl;
19227 const uint8_t * rdataPtr;
19228 size_t rdataLen;
19229 char * rdataStr;
19230 Boolean cacheFlush;
19231
19232 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
19233 require_noerr( err, exit );
19234
19235 err = DomainNameToString( name, NULL, nameStr, NULL );
19236 require_noerr( err, exit );
19237
19238 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
19239 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
19240
19241 rdataStr = NULL;
19242 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
19243 if( !rdataStr )
19244 {
19245 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, (int) rdataLen );
19246 require_action( rdataStr, exit, err = kNoMemoryErr );
19247 }
19248
19249 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
19250 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
19251 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
19252
19253 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19254 nameStr, ttl, cacheFlush ? "CF" : "",
19255 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
19256 RecordTypeToString( type ), rdataStr );
19257 free( rdataStr );
19258 }
19259 _Append( "\n" );
19260
19261 err = DataBuffer_Append( &dataBuf, "", 1 );
19262 require_noerr( err, exit );
19263
19264 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
19265 require_noerr( err, exit );
19266
19267 exit:
19268 DataBuffer_Free( &dataBuf );
19269 return( err );
19270 }
19271
19272 //===========================================================================================================================
19273 // WriteDNSQueryMessage
19274 //===========================================================================================================================
19275
19276 static OSStatus
19277 WriteDNSQueryMessage(
19278 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
19279 uint16_t inMsgID,
19280 uint16_t inFlags,
19281 const char * inQName,
19282 uint16_t inQType,
19283 uint16_t inQClass,
19284 size_t * outMsgLen )
19285 {
19286 OSStatus err;
19287 uint8_t qname[ kDomainNameLengthMax ];
19288
19289 err = DomainNameFromString( qname, inQName, NULL );
19290 require_noerr_quiet( err, exit );
19291
19292 err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
19293 require_noerr_quiet( err, exit );
19294
19295 exit:
19296 return( err );
19297 }
19298
19299 //===========================================================================================================================
19300 // DispatchSignalSourceCreate
19301 //===========================================================================================================================
19302
19303 static OSStatus
19304 DispatchSignalSourceCreate(
19305 int inSignal,
19306 DispatchHandler inEventHandler,
19307 void * inContext,
19308 dispatch_source_t * outSource )
19309 {
19310 OSStatus err;
19311 dispatch_source_t source;
19312
19313 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
19314 require_action( source, exit, err = kUnknownErr );
19315
19316 dispatch_set_context( source, inContext );
19317 dispatch_source_set_event_handler_f( source, inEventHandler );
19318
19319 *outSource = source;
19320 err = kNoErr;
19321
19322 exit:
19323 return( err );
19324 }
19325
19326 //===========================================================================================================================
19327 // DispatchSocketSourceCreate
19328 //===========================================================================================================================
19329
19330 static OSStatus
19331 DispatchSocketSourceCreate(
19332 SocketRef inSock,
19333 dispatch_source_type_t inType,
19334 dispatch_queue_t inQueue,
19335 DispatchHandler inEventHandler,
19336 DispatchHandler inCancelHandler,
19337 void * inContext,
19338 dispatch_source_t * outSource )
19339 {
19340 OSStatus err;
19341 dispatch_source_t source;
19342
19343 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19344 require_action( source, exit, err = kUnknownErr );
19345
19346 dispatch_set_context( source, inContext );
19347 dispatch_source_set_event_handler_f( source, inEventHandler );
19348 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
19349
19350 *outSource = source;
19351 err = kNoErr;
19352
19353 exit:
19354 return( err );
19355 }
19356
19357 //===========================================================================================================================
19358 // DispatchTimerCreate
19359 //===========================================================================================================================
19360
19361 static OSStatus
19362 DispatchTimerCreate(
19363 dispatch_time_t inStart,
19364 uint64_t inIntervalNs,
19365 uint64_t inLeewayNs,
19366 dispatch_queue_t inQueue,
19367 DispatchHandler inEventHandler,
19368 DispatchHandler inCancelHandler,
19369 void * inContext,
19370 dispatch_source_t * outTimer )
19371 {
19372 OSStatus err;
19373 dispatch_source_t timer;
19374
19375 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19376 require_action( timer, exit, err = kUnknownErr );
19377
19378 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
19379 dispatch_set_context( timer, inContext );
19380 dispatch_source_set_event_handler_f( timer, inEventHandler );
19381 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
19382
19383 *outTimer = timer;
19384 err = kNoErr;
19385
19386 exit:
19387 return( err );
19388 }
19389
19390 //===========================================================================================================================
19391 // DispatchProcessMonitorCreate
19392 //===========================================================================================================================
19393
19394 static OSStatus
19395 DispatchProcessMonitorCreate(
19396 pid_t inPID,
19397 unsigned long inFlags,
19398 dispatch_queue_t inQueue,
19399 DispatchHandler inEventHandler,
19400 DispatchHandler inCancelHandler,
19401 void * inContext,
19402 dispatch_source_t * outMonitor )
19403 {
19404 OSStatus err;
19405 dispatch_source_t monitor;
19406
19407 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
19408 inQueue ? inQueue : dispatch_get_main_queue() );
19409 require_action( monitor, exit, err = kUnknownErr );
19410
19411 dispatch_set_context( monitor, inContext );
19412 dispatch_source_set_event_handler_f( monitor, inEventHandler );
19413 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
19414
19415 *outMonitor = monitor;
19416 err = kNoErr;
19417
19418 exit:
19419 return( err );
19420 }
19421
19422 //===========================================================================================================================
19423 // ServiceTypeDescription
19424 //===========================================================================================================================
19425
19426 typedef struct
19427 {
19428 const char * name; // Name of the service type in two-label "_service._proto" format.
19429 const char * description; // Description of the service type.
19430
19431 } ServiceType;
19432
19433 // A Non-comprehensive table of DNS-SD service types
19434
19435 static const ServiceType kServiceTypes[] =
19436 {
19437 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19438 { "_adisk._tcp", "Automatic Disk Discovery" },
19439 { "_afpovertcp._tcp", "Apple File Sharing" },
19440 { "_airdrop._tcp", "AirDrop" },
19441 { "_airplay._tcp", "AirPlay" },
19442 { "_airport._tcp", "AirPort Base Station" },
19443 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19444 { "_eppc._tcp", "Remote AppleEvents" },
19445 { "_ftp._tcp", "File Transfer Protocol" },
19446 { "_home-sharing._tcp", "Home Sharing" },
19447 { "_homekit._tcp", "HomeKit" },
19448 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19449 { "_https._tcp", "HTTP over SSL/TLS" },
19450 { "_ipp._tcp", "Internet Printing Protocol" },
19451 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19452 { "_mediaremotetv._tcp", "Media Remote" },
19453 { "_net-assistant._tcp", "Apple Remote Desktop" },
19454 { "_od-master._tcp", "OpenDirectory Master" },
19455 { "_nfs._tcp", "Network File System" },
19456 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19457 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19458 { "_raop._tcp", "Remote Audio Output Protocol" },
19459 { "_rfb._tcp", "Remote Frame Buffer" },
19460 { "_scanner._tcp", "Bonjour Scanning" },
19461 { "_smb._tcp", "Server Message Block over TCP/IP" },
19462 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19463 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19464 { "_ssh._tcp", "SSH Remote Login Protocol" },
19465 { "_teleport._tcp", "teleport" },
19466 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19467 { "_workstation._tcp", "Workgroup Manager" },
19468 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19469 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19470 };
19471
19472 static const char * ServiceTypeDescription( const char *inName )
19473 {
19474 const ServiceType * serviceType;
19475 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
19476
19477 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
19478 {
19479 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
19480 {
19481 const char * const ptr = &inName[ strlen( serviceType->name ) ];
19482
19483 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
19484 {
19485 return( serviceType->description );
19486 }
19487 }
19488 }
19489 return( NULL );
19490 }
19491
19492 //===========================================================================================================================
19493 // SocketContextCreate
19494 //===========================================================================================================================
19495
19496 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
19497 {
19498 OSStatus err;
19499 SocketContext * context;
19500
19501 context = (SocketContext *) calloc( 1, sizeof( *context ) );
19502 require_action( context, exit, err = kNoMemoryErr );
19503
19504 context->refCount = 1;
19505 context->sock = inSock;
19506 context->userContext = inUserContext;
19507
19508 *outContext = context;
19509 err = kNoErr;
19510
19511 exit:
19512 return( err );
19513 }
19514
19515 //===========================================================================================================================
19516 // SocketContextRetain
19517 //===========================================================================================================================
19518
19519 static SocketContext * SocketContextRetain( SocketContext *inContext )
19520 {
19521 ++inContext->refCount;
19522 return( inContext );
19523 }
19524
19525 //===========================================================================================================================
19526 // SocketContextRelease
19527 //===========================================================================================================================
19528
19529 static void SocketContextRelease( SocketContext *inContext )
19530 {
19531 if( --inContext->refCount == 0 )
19532 {
19533 ForgetSocket( &inContext->sock );
19534 free( inContext );
19535 }
19536 }
19537
19538 //===========================================================================================================================
19539 // SocketContextCancelHandler
19540 //===========================================================================================================================
19541
19542 static void SocketContextCancelHandler( void *inContext )
19543 {
19544 SocketContextRelease( (SocketContext *) inContext );
19545 }
19546
19547 //===========================================================================================================================
19548 // StringToInt32
19549 //===========================================================================================================================
19550
19551 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
19552 {
19553 OSStatus err;
19554 long value;
19555 char * endPtr;
19556
19557 value = strtol( inString, &endPtr, 0 );
19558 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19559 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
19560
19561 *outValue = (int32_t) value;
19562 err = kNoErr;
19563
19564 exit:
19565 return( err );
19566 }
19567
19568 //===========================================================================================================================
19569 // StringToUInt32
19570 //===========================================================================================================================
19571
19572 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
19573 {
19574 OSStatus err;
19575 uint32_t value;
19576 char * endPtr;
19577
19578 value = (uint32_t) strtol( inString, &endPtr, 0 );
19579 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19580
19581 *outValue = value;
19582 err = kNoErr;
19583
19584 exit:
19585 return( err );
19586 }
19587
19588 #if( TARGET_OS_DARWIN )
19589 //===========================================================================================================================
19590 // _StringToInt64
19591 //===========================================================================================================================
19592
19593 static int64_t _StringToInt64( const char *inString, OSStatus *outError )
19594 {
19595 OSStatus err;
19596 long long val;
19597 char * end;
19598 int errnoVal;
19599
19600 set_errno_compat( 0 );
19601 val = strtoll( inString, &end, 0 );
19602 errnoVal = errno_compat();
19603
19604 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19605 require_action_quiet( ( ( val != LLONG_MIN ) && ( val != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19606 require_action_quiet( ( val >= INT64_MIN ) && ( val <= INT64_MAX ), exit, err = kRangeErr );
19607 err = kNoErr;
19608
19609 exit:
19610 if( outError ) *outError = err;
19611 return( (int64_t)val );
19612 }
19613
19614 //===========================================================================================================================
19615 // _StringToUInt64
19616 //===========================================================================================================================
19617
19618 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError )
19619 {
19620 OSStatus err;
19621 unsigned long long val;
19622 char * end;
19623 int errnoVal;
19624
19625 set_errno_compat( 0 );
19626 val = strtoull( inString, &end, 0 );
19627 errnoVal = errno_compat();
19628
19629 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19630 require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19631 require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
19632 err = kNoErr;
19633
19634 exit:
19635 if( outError ) *outError = err;
19636 return( (uint64_t)val );
19637 }
19638
19639 //===========================================================================================================================
19640 // _StringToPID
19641 //===========================================================================================================================
19642
19643 static pid_t _StringToPID( const char *inString, OSStatus *outError )
19644 {
19645 OSStatus err;
19646 int64_t val;
19647
19648 val = _StringToInt64( inString, &err );
19649 require_noerr_quiet( err, exit );
19650 require_action_quiet( val == (pid_t) val, exit, err = kRangeErr );
19651 err = kNoErr;
19652
19653 exit:
19654 if( outError ) *outError = err;
19655 return( (pid_t) val );
19656 }
19657
19658 //===========================================================================================================================
19659 // _ParseEscapedString
19660 //
19661 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19662 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19663 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19664 //===========================================================================================================================
19665
19666 static OSStatus
19667 _ParseEscapedString(
19668 const char * inSrc,
19669 const char * inEnd,
19670 const char * inDelimiters,
19671 char * inBufPtr,
19672 size_t inBufLen,
19673 size_t * outCopiedLen,
19674 size_t * outActualLen,
19675 const char ** outPtr )
19676 {
19677 OSStatus err;
19678 const char * ptr;
19679 char * dst = inBufPtr;
19680 const char * const lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
19681 size_t len;
19682
19683 len = 0;
19684 ptr = inSrc;
19685 if( !inDelimiters ) inDelimiters = "";
19686 while( ptr < inEnd )
19687 {
19688 int c;
19689 const char * del;
19690
19691 c = *ptr;
19692 for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
19693 if( *del != '\0' ) break;
19694 ++ptr;
19695 if( c == '\\' )
19696 {
19697 require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
19698 c = *ptr++;
19699 }
19700 ++len;
19701 if( dst < lim ) *dst++ = (char) c;
19702 }
19703 if( inBufLen > 0 ) *dst = '\0';
19704
19705 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBufPtr );
19706 if( outActualLen ) *outActualLen = len;
19707 if( outPtr ) *outPtr = ptr;
19708 err = kNoErr;
19709
19710 exit:
19711 return( err );
19712 }
19713 #endif
19714
19715 //===========================================================================================================================
19716 // StringToARecordData
19717 //===========================================================================================================================
19718
19719 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19720 {
19721 OSStatus err;
19722 uint32_t * addrPtr;
19723 const size_t addrLen = sizeof( *addrPtr );
19724 const char * end;
19725
19726 addrPtr = (uint32_t *) malloc( addrLen );
19727 require_action( addrPtr, exit, err = kNoMemoryErr );
19728
19729 err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
19730 NULL, NULL, NULL, &end );
19731 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19732 require_noerr_quiet( err, exit );
19733
19734 *addrPtr = HostToBig32( *addrPtr );
19735
19736 *outPtr = (uint8_t *) addrPtr;
19737 addrPtr = NULL;
19738 *outLen = addrLen;
19739
19740 exit:
19741 FreeNullSafe( addrPtr );
19742 return( err );
19743 }
19744
19745 //===========================================================================================================================
19746 // StringToAAAARecordData
19747 //===========================================================================================================================
19748
19749 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19750 {
19751 OSStatus err;
19752 uint8_t * addrPtr;
19753 const size_t addrLen = 16;
19754 const char * end;
19755
19756 addrPtr = (uint8_t *) malloc( addrLen );
19757 require_action( addrPtr, exit, err = kNoMemoryErr );
19758
19759 err = _StringToIPv6Address( inString,
19760 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
19761 addrPtr, NULL, NULL, NULL, &end );
19762 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19763 require_noerr_quiet( err, exit );
19764
19765 *outPtr = addrPtr;
19766 addrPtr = NULL;
19767 *outLen = addrLen;
19768
19769 exit:
19770 FreeNullSafe( addrPtr );
19771 return( err );
19772 }
19773
19774 //===========================================================================================================================
19775 // StringToDomainName
19776 //===========================================================================================================================
19777
19778 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
19779 {
19780 OSStatus err;
19781 uint8_t * namePtr;
19782 size_t nameLen;
19783 uint8_t * end;
19784 uint8_t nameBuf[ kDomainNameLengthMax ];
19785
19786 err = DomainNameFromString( nameBuf, inString, &end );
19787 require_noerr_quiet( err, exit );
19788
19789 nameLen = (size_t)( end - nameBuf );
19790 namePtr = _memdup( nameBuf, nameLen );
19791 require_action( namePtr, exit, err = kNoMemoryErr );
19792
19793 *outPtr = namePtr;
19794 namePtr = NULL;
19795 if( outLen ) *outLen = nameLen;
19796
19797 exit:
19798 return( err );
19799 }
19800
19801 #if( TARGET_OS_DARWIN )
19802 //===========================================================================================================================
19803 // GetDefaultDNSServer
19804 //===========================================================================================================================
19805
19806 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
19807 {
19808 OSStatus err;
19809 dns_config_t * config;
19810 struct sockaddr * addr;
19811 int32_t i;
19812
19813 config = dns_configuration_copy();
19814 require_action( config, exit, err = kUnknownErr );
19815
19816 addr = NULL;
19817 for( i = 0; i < config->n_resolver; ++i )
19818 {
19819 const dns_resolver_t * const resolver = config->resolver[ i ];
19820
19821 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
19822 {
19823 addr = resolver->nameserver[ 0 ];
19824 break;
19825 }
19826 }
19827 require_action_quiet( addr, exit, err = kNotFoundErr );
19828
19829 SockAddrCopy( addr, outAddr );
19830 err = kNoErr;
19831
19832 exit:
19833 if( config ) dns_configuration_free( config );
19834 return( err );
19835 }
19836 #endif
19837
19838 //===========================================================================================================================
19839 // GetMDNSMulticastAddrV4
19840 //===========================================================================================================================
19841
19842 static void _MDNSMulticastAddrV4Init( void *inContext );
19843
19844 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
19845 {
19846 static struct sockaddr_in sMDNSMulticastAddrV4;
19847 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
19848
19849 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
19850 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
19851 }
19852
19853 static void _MDNSMulticastAddrV4Init( void *inContext )
19854 {
19855 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
19856
19857 memset( addr, 0, sizeof( *addr ) );
19858 SIN_LEN_SET( addr );
19859 addr->sin_family = AF_INET;
19860 addr->sin_port = htons( kMDNSPort );
19861 addr->sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19862 }
19863
19864 //===========================================================================================================================
19865 // GetMDNSMulticastAddrV6
19866 //===========================================================================================================================
19867
19868 static void _MDNSMulticastAddrV6Init( void *inContext );
19869
19870 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
19871 {
19872 static struct sockaddr_in6 sMDNSMulticastAddrV6;
19873 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
19874
19875 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
19876 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
19877 }
19878
19879 static void _MDNSMulticastAddrV6Init( void *inContext )
19880 {
19881 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
19882
19883 memset( addr, 0, sizeof( *addr ) );
19884 SIN6_LEN_SET( addr );
19885 addr->sin6_family = AF_INET6;
19886 addr->sin6_port = htons( kMDNSPort );
19887 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19888 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
19889 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
19890 }
19891
19892 //===========================================================================================================================
19893 // CreateMulticastSocket
19894 //===========================================================================================================================
19895
19896 static OSStatus
19897 CreateMulticastSocket(
19898 const struct sockaddr * inAddr,
19899 int inPort,
19900 const char * inIfName,
19901 uint32_t inIfIndex,
19902 Boolean inJoin,
19903 int * outPort,
19904 SocketRef * outSock )
19905 {
19906 OSStatus err;
19907 SocketRef sock = kInvalidSocketRef;
19908 const int family = inAddr->sa_family;
19909 int port;
19910
19911 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
19912
19913 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
19914 require_noerr_quiet( err, exit );
19915
19916 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
19917 require_noerr_quiet( err, exit );
19918
19919 if( family == AF_INET )
19920 {
19921 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
19922 err = map_socket_noerr_errno( sock, err );
19923 require_noerr_quiet( err, exit );
19924 }
19925 else
19926 {
19927 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
19928 err = map_socket_noerr_errno( sock, err );
19929 require_noerr_quiet( err, exit );
19930 }
19931
19932 if( inJoin )
19933 {
19934 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
19935 require_noerr_quiet( err, exit );
19936 }
19937
19938 if( outPort ) *outPort = port;
19939 *outSock = sock;
19940 sock = kInvalidSocketRef;
19941
19942 exit:
19943 ForgetSocket( &sock );
19944 return( err );
19945 }
19946
19947 //===========================================================================================================================
19948 // DecimalTextToUInt32
19949 //===========================================================================================================================
19950
19951 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
19952 {
19953 OSStatus err;
19954 uint64_t value;
19955 const char * ptr = inSrc;
19956
19957 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
19958
19959 value = (uint64_t)( *ptr++ - '0' );
19960 if( value == 0 )
19961 {
19962 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
19963 {
19964 err = kMalformedErr;
19965 goto exit;
19966 }
19967 }
19968 else
19969 {
19970 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
19971 {
19972 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
19973 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
19974 }
19975 }
19976
19977 *outValue = (uint32_t) value;
19978 if( outPtr ) *outPtr = ptr;
19979 err = kNoErr;
19980
19981 exit:
19982 return( err );
19983 }
19984
19985 //===========================================================================================================================
19986 // CheckIntegerArgument
19987 //===========================================================================================================================
19988
19989 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
19990 {
19991 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
19992
19993 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
19994 return( kRangeErr );
19995 }
19996
19997 //===========================================================================================================================
19998 // CheckDoubleArgument
19999 //===========================================================================================================================
20000
20001 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
20002 {
20003 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
20004
20005 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
20006 return( kRangeErr );
20007 }
20008
20009 //===========================================================================================================================
20010 // CheckRootUser
20011 //===========================================================================================================================
20012
20013 static OSStatus CheckRootUser( void )
20014 {
20015 if( geteuid() == 0 ) return( kNoErr );
20016
20017 FPrintF( stderr, "error: This command must to be run as root.\n" );
20018 return( kPermissionErr );
20019 }
20020
20021 //===========================================================================================================================
20022 // SpawnCommand
20023 //
20024 // Note: Based on systemf() from CoreUtils framework.
20025 //===========================================================================================================================
20026
20027 extern char ** environ;
20028
20029 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... )
20030 {
20031 OSStatus err;
20032 va_list args;
20033 char * command;
20034 char * argv[ 4 ];
20035 pid_t pid;
20036
20037 command = NULL;
20038 va_start( args, inFormat );
20039 VASPrintF( &command, inFormat, args );
20040 va_end( args );
20041 require_action( command, exit, err = kUnknownErr );
20042
20043 argv[ 0 ] = "/bin/sh";
20044 argv[ 1 ] = "-c";
20045 argv[ 2 ] = command;
20046 argv[ 3 ] = NULL;
20047 err = posix_spawn( &pid, argv[ 0 ], NULL, NULL, argv, environ );
20048 free( command );
20049 require_noerr_quiet( err, exit );
20050
20051 if( outPID ) *outPID = pid;
20052
20053 exit:
20054 return( err );
20055 }
20056
20057 //===========================================================================================================================
20058 // OutputFormatFromArgString
20059 //===========================================================================================================================
20060
20061 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
20062 {
20063 OSStatus err;
20064 OutputFormatType format;
20065
20066 format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
20067 kOutputFormatStr_JSON, kOutputFormatType_JSON,
20068 kOutputFormatStr_XML, kOutputFormatType_XML,
20069 kOutputFormatStr_Binary, kOutputFormatType_Binary,
20070 NULL );
20071 if( outFormat ) *outFormat = format;
20072 return( err );
20073 }
20074
20075 //===========================================================================================================================
20076 // OutputPropertyList
20077 //===========================================================================================================================
20078
20079 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
20080 {
20081 OSStatus err;
20082 CFDataRef results = NULL;
20083 FILE * file = NULL;
20084
20085 // Convert plist to a specific format.
20086
20087 switch( inType )
20088 {
20089 case kOutputFormatType_JSON:
20090 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
20091 require_action( results, exit, err = kUnknownErr );
20092 break;
20093
20094 case kOutputFormatType_XML:
20095 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
20096 require_action( results, exit, err = kUnknownErr );
20097 break;
20098
20099 case kOutputFormatType_Binary:
20100 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
20101 require_action( results, exit, err = kUnknownErr );
20102 break;
20103
20104 default:
20105 err = kTypeErr;
20106 goto exit;
20107 }
20108
20109 // Write formatted results to file or stdout.
20110
20111 if( inOutputFilePath )
20112 {
20113 file = fopen( inOutputFilePath, "wb" );
20114 err = map_global_value_errno( file, file );
20115 require_noerr( err, exit );
20116 }
20117 else
20118 {
20119 file = stdout;
20120 }
20121
20122 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
20123 require_noerr_quiet( err, exit );
20124
20125 // Write a trailing newline for JSON-formatted results.
20126
20127 if( inType == kOutputFormatType_JSON )
20128 {
20129 err = WriteANSIFile( file, "\n", 1 );
20130 require_noerr_quiet( err, exit );
20131 }
20132
20133 exit:
20134 if( file && ( file != stdout ) ) fclose( file );
20135 CFReleaseNullSafe( results );
20136 return( err );
20137 }
20138
20139 //===========================================================================================================================
20140 // CreateSRVRecordDataFromString
20141 //===========================================================================================================================
20142
20143 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
20144 {
20145 OSStatus err;
20146 DataBuffer dataBuf;
20147 const char * ptr;
20148 int i;
20149 uint8_t * end;
20150 uint8_t target[ kDomainNameLengthMax ];
20151
20152 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
20153
20154 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20155
20156 ptr = inString;
20157 for( i = 0; i < 3; ++i )
20158 {
20159 char * next;
20160 long value;
20161 uint8_t buf[ 2 ];
20162
20163 value = strtol( ptr, &next, 0 );
20164 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
20165 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
20166 ptr = next + 1;
20167
20168 WriteBig16( buf, value );
20169
20170 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
20171 require_noerr( err, exit );
20172 }
20173
20174 // Set the target domain name.
20175
20176 err = DomainNameFromString( target, ptr, &end );
20177 require_noerr_quiet( err, exit );
20178
20179 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
20180 require_noerr( err, exit );
20181
20182 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20183 require_noerr( err, exit );
20184
20185 exit:
20186 DataBuffer_Free( &dataBuf );
20187 return( err );
20188 }
20189
20190 //===========================================================================================================================
20191 // CreateTXTRecordDataFromString
20192 //===========================================================================================================================
20193
20194 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
20195 {
20196 OSStatus err;
20197 DataBuffer dataBuf;
20198 const char * src;
20199 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20200
20201 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
20202
20203 src = inString;
20204 for( ;; )
20205 {
20206 uint8_t * dst = &txtStr[ 1 ];
20207 const uint8_t * const lim = &txtStr[ 256 ];
20208 int c;
20209
20210 while( *src && ( *src != inDelimiter ) )
20211 {
20212 if( ( c = *src++ ) == '\\' )
20213 {
20214 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
20215 c = *src++;
20216 }
20217 require_action_quiet( dst < lim, exit, err = kOverrunErr );
20218 *dst++ = (uint8_t) c;
20219 }
20220 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
20221 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
20222 require_noerr( err, exit );
20223
20224 if( *src == '\0' ) break;
20225 ++src;
20226 }
20227
20228 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20229 require_noerr( err, exit );
20230
20231 exit:
20232 DataBuffer_Free( &dataBuf );
20233 return( err );
20234 }
20235
20236 //===========================================================================================================================
20237 // CreateNSECRecordData
20238 //===========================================================================================================================
20239
20240 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
20241 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
20242
20243 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20244
20245 static OSStatus
20246 CreateNSECRecordData(
20247 const uint8_t * inNextDomainName,
20248 uint8_t ** outPtr,
20249 size_t * outLen,
20250 unsigned int inTypeCount,
20251 ... )
20252 {
20253 OSStatus err;
20254 va_list args;
20255 DataBuffer rdataDB;
20256 unsigned int * array = NULL;
20257 unsigned int i, type, maxBit, currBlock, bitmapLen;
20258 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
20259 uint8_t * const bitmap = &fields[ 2 ];
20260
20261 va_start( args, inTypeCount );
20262 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20263
20264 // Append Next Domain Name.
20265
20266 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
20267 require_noerr( err, exit );
20268
20269 // Append Type Bit Maps.
20270
20271 maxBit = 0;
20272 memset( bitmap, 0, kNSECBitmapMaxLength );
20273 if( inTypeCount > 0 )
20274 {
20275 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
20276 require_action( array, exit, err = kNoMemoryErr );
20277
20278 for( i = 0; i < inTypeCount; ++i )
20279 {
20280 type = va_arg( args, unsigned int );
20281 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
20282 array[ i ] = type;
20283 }
20284 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
20285
20286 currBlock = array[ 0 ] / 256;
20287 for( i = 0; i < inTypeCount; ++i )
20288 {
20289 const unsigned int block = array[ i ] / 256;
20290 const unsigned int bit = array[ i ] % 256;
20291
20292 if( block != currBlock )
20293 {
20294 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20295 fields[ 0 ] = (uint8_t) currBlock;
20296 fields[ 1 ] = (uint8_t) bitmapLen;
20297
20298 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20299 require_noerr( err, exit );
20300
20301 maxBit = 0;
20302 currBlock = block;
20303 memset( bitmap, 0, bitmapLen );
20304 }
20305 BitArray_SetBit( bitmap, bit );
20306 if( bit > maxBit ) maxBit = bit;
20307 }
20308 }
20309 else
20310 {
20311 currBlock = 0;
20312 }
20313
20314 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20315 fields[ 0 ] = (uint8_t) currBlock;
20316 fields[ 1 ] = (uint8_t) bitmapLen;
20317
20318 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20319 require_noerr( err, exit );
20320
20321 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20322 require_noerr( err, exit );
20323
20324 exit:
20325 va_end( args );
20326 DataBuffer_Free( &rdataDB );
20327 FreeNullSafe( array );
20328 return( err );
20329 }
20330
20331 //===========================================================================================================================
20332 // AppendSOARecord
20333 //===========================================================================================================================
20334
20335 static OSStatus
20336 _AppendSOARecordData(
20337 DataBuffer * inDB,
20338 const uint8_t * inMName,
20339 const uint8_t * inRName,
20340 uint32_t inSerial,
20341 uint32_t inRefresh,
20342 uint32_t inRetry,
20343 uint32_t inExpire,
20344 uint32_t inMinimumTTL,
20345 size_t * outLen );
20346
20347 static OSStatus
20348 AppendSOARecord(
20349 DataBuffer * inDB,
20350 const uint8_t * inNamePtr,
20351 size_t inNameLen,
20352 uint16_t inType,
20353 uint16_t inClass,
20354 uint32_t inTTL,
20355 const uint8_t * inMName,
20356 const uint8_t * inRName,
20357 uint32_t inSerial,
20358 uint32_t inRefresh,
20359 uint32_t inRetry,
20360 uint32_t inExpire,
20361 uint32_t inMinimumTTL,
20362 size_t * outLen )
20363 {
20364 OSStatus err;
20365 size_t rdataLen;
20366 size_t rdlengthOffset = 0;
20367 uint8_t * rdlengthPtr;
20368
20369 if( inDB )
20370 {
20371 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
20372 require_noerr( err, exit );
20373
20374 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
20375 }
20376
20377 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
20378 require_noerr( err, exit );
20379
20380 if( inDB )
20381 {
20382 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
20383 WriteBig16( rdlengthPtr, rdataLen );
20384 }
20385
20386 if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
20387 err = kNoErr;
20388
20389 exit:
20390 return( err );
20391 }
20392
20393 static OSStatus
20394 _AppendSOARecordData(
20395 DataBuffer * inDB,
20396 const uint8_t * inMName,
20397 const uint8_t * inRName,
20398 uint32_t inSerial,
20399 uint32_t inRefresh,
20400 uint32_t inRetry,
20401 uint32_t inExpire,
20402 uint32_t inMinimumTTL,
20403 size_t * outLen )
20404 {
20405 OSStatus err;
20406 dns_fixed_fields_soa fields;
20407 const size_t mnameLen = DomainNameLength( inMName );
20408 const size_t rnameLen = DomainNameLength( inRName );
20409
20410 if( inDB )
20411 {
20412 err = DataBuffer_Append( inDB, inMName, mnameLen );
20413 require_noerr( err, exit );
20414
20415 err = DataBuffer_Append( inDB, inRName, rnameLen );
20416 require_noerr( err, exit );
20417
20418 dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
20419 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20420 require_noerr( err, exit );
20421 }
20422 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
20423 err = kNoErr;
20424
20425 exit:
20426 return( err );
20427 }
20428
20429 //===========================================================================================================================
20430 // CreateSOARecordData
20431 //===========================================================================================================================
20432
20433 static OSStatus
20434 CreateSOARecordData(
20435 const uint8_t * inMName,
20436 const uint8_t * inRName,
20437 uint32_t inSerial,
20438 uint32_t inRefresh,
20439 uint32_t inRetry,
20440 uint32_t inExpire,
20441 uint32_t inMinimumTTL,
20442 uint8_t ** outPtr,
20443 size_t * outLen )
20444 {
20445 OSStatus err;
20446 DataBuffer rdataDB;
20447
20448 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20449
20450 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
20451 require_noerr( err, exit );
20452
20453 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20454 require_noerr( err, exit );
20455
20456 exit:
20457 DataBuffer_Free( &rdataDB );
20458 return( err );
20459 }
20460
20461 //===========================================================================================================================
20462 // _DataBuffer_AppendDNSQuestion
20463 //===========================================================================================================================
20464
20465 static OSStatus
20466 _DataBuffer_AppendDNSQuestion(
20467 DataBuffer * inDB,
20468 const uint8_t * inNamePtr,
20469 size_t inNameLen,
20470 uint16_t inType,
20471 uint16_t inClass )
20472 {
20473 OSStatus err;
20474 dns_fixed_fields_question fields;
20475
20476 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20477 require_noerr( err, exit );
20478
20479 dns_fixed_fields_question_init( &fields, inType, inClass );
20480 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20481 require_noerr( err, exit );
20482
20483 exit:
20484 return( err );
20485 }
20486
20487 //===========================================================================================================================
20488 // _DataBuffer_AppendDNSRecord
20489 //===========================================================================================================================
20490
20491 static OSStatus
20492 _DataBuffer_AppendDNSRecord(
20493 DataBuffer * inDB,
20494 const uint8_t * inNamePtr,
20495 size_t inNameLen,
20496 uint16_t inType,
20497 uint16_t inClass,
20498 uint32_t inTTL,
20499 const uint8_t * inRDataPtr,
20500 size_t inRDataLen )
20501 {
20502 OSStatus err;
20503 dns_fixed_fields_record fields;
20504
20505 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
20506
20507 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20508 require_noerr( err, exit );
20509
20510 dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
20511 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20512 require_noerr( err, exit );
20513
20514 if( inRDataPtr )
20515 {
20516 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
20517 require_noerr( err, exit );
20518 }
20519
20520 exit:
20521 return( err );
20522 }
20523
20524 //===========================================================================================================================
20525 // _NanoTime64ToTimestamp
20526 //===========================================================================================================================
20527
20528 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
20529 {
20530 struct timeval tv;
20531
20532 NanoTimeToTimeVal( inTime, &tv );
20533 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
20534 }
20535
20536 //===========================================================================================================================
20537 // _MDNSInterfaceListCreate
20538 //===========================================================================================================================
20539
20540 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
20541
20542 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
20543 {
20544 OSStatus err;
20545 struct ifaddrs * ifaList;
20546 const struct ifaddrs * ifa;
20547 MDNSInterfaceItem * interfaceList;
20548 MDNSInterfaceItem ** ptr;
20549 SocketRef infoSock;
20550
20551 ifaList = NULL;
20552 interfaceList = NULL;
20553 infoSock = kInvalidSocketRef;
20554 if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
20555 require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
20556
20557 infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
20558 err = map_socket_creation_errno( infoSock );
20559 require_noerr( err, exit );
20560
20561 err = getifaddrs( &ifaList );
20562 err = map_global_noerr_errno( err );
20563 require_noerr( err, exit );
20564
20565 ptr = &interfaceList;
20566 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
20567 {
20568 MDNSInterfaceItem * item;
20569 int family;
20570 const unsigned int flagsMask = IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
20571 const unsigned int flagsNeeded = IFF_UP | IFF_MULTICAST;
20572
20573 if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded ) continue;
20574 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
20575 family = ifa->ifa_addr->sa_family;
20576 if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
20577
20578 for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
20579 if( !item )
20580 {
20581 NetTransportType type;
20582 uint32_t ifIndex;
20583 const char * const ifName = ifa->ifa_name;
20584
20585 if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
20586 err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
20587 require_noerr( err, exit );
20588
20589 if( ifIndex == 0 ) continue;
20590 if( type == kNetTransportType_AWDL )
20591 {
20592 if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
20593 }
20594 else
20595 {
20596 if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
20597 }
20598 item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
20599 require_action( item, exit, err = kNoMemoryErr );
20600
20601 *ptr = item;
20602 ptr = &item->next;
20603
20604 item->ifName = strdup( ifName );
20605 require_action( item->ifName, exit, err = kNoMemoryErr );
20606
20607 item->ifIndex = ifIndex;
20608 if( type == kNetTransportType_AWDL ) item->isAWDL = true;
20609 else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
20610 }
20611 if( family == AF_INET ) item->hasIPv4 = true;
20612 else item->hasIPv6 = true;
20613 }
20614 require_action_quiet( interfaceList, exit, err = kNotFoundErr );
20615
20616 if( outList )
20617 {
20618 *outList = interfaceList;
20619 interfaceList = NULL;
20620 }
20621
20622 exit:
20623 if( ifaList ) freeifaddrs( ifaList );
20624 _MDNSInterfaceListFree( interfaceList );
20625 ForgetSocket( &infoSock );
20626 return( err );
20627 }
20628
20629 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
20630 {
20631 OSStatus err;
20632 int i;
20633 static const char * const kMDNSInterfacePrefixBlacklist[] = { "llw" };
20634 struct ifreq ifr;
20635
20636 // Check if the interface name's prefix matches the prefix blacklist.
20637
20638 for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
20639 {
20640 const char * const prefix = kMDNSInterfacePrefixBlacklist[ i ];
20641
20642 if( strcmp_prefix( inIfName, prefix ) == 0 )
20643 {
20644 const char * ptr = &inIfName[ strlen( prefix ) ];
20645
20646 while( isdigit_safe( *ptr ) ) ++ptr;
20647 if( *ptr == '\0' ) return( true );
20648 }
20649 }
20650
20651 // Check if the interface is used for inter-(co)processor networking.
20652
20653 memset( &ifr, 0, sizeof( ifr ) );
20654 strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
20655 err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
20656 err = map_global_value_errno( err != -1, err );
20657 if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
20658
20659 return( false );
20660 }
20661
20662 //===========================================================================================================================
20663 // _MDNSInterfaceListFree
20664 //===========================================================================================================================
20665
20666 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList )
20667 {
20668 MDNSInterfaceItem * item;
20669
20670 while( ( item = inList ) != NULL )
20671 {
20672 inList = item->next;
20673 FreeNullSafe( item->ifName );
20674 free( item );
20675 }
20676 }
20677
20678 //===========================================================================================================================
20679 // _MDNSInterfaceGetAny
20680 //===========================================================================================================================
20681
20682 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
20683 {
20684 OSStatus err;
20685 MDNSInterfaceItem * list;
20686 const MDNSInterfaceItem * item;
20687
20688 list = NULL;
20689 err = _MDNSInterfaceListCreate( inSubset, 0, &list );
20690 require_noerr_quiet( err, exit );
20691 require_action_quiet( list, exit, err = kNotFoundErr );
20692
20693 for( item = list; item; item = item->next )
20694 {
20695 if( item->hasIPv4 && item->hasIPv6 ) break;
20696 }
20697 if( !item ) item = list;
20698 if( inNameBuf ) strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
20699 if( outIndex ) *outIndex = item->ifIndex;
20700
20701 exit:
20702 _MDNSInterfaceListFree( list );
20703 return( err );
20704 }
20705
20706 //===========================================================================================================================
20707 // _SetComputerName
20708 //===========================================================================================================================
20709
20710 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
20711 {
20712 OSStatus err;
20713 SCPreferencesRef prefs;
20714 Boolean ok;
20715
20716 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20717 err = map_scerror( prefs );
20718 require_noerr_quiet( err, exit );
20719
20720 ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
20721 err = map_scerror( ok );
20722 require_noerr_quiet( err, exit );
20723
20724 ok = SCPreferencesCommitChanges( prefs );
20725 err = map_scerror( ok );
20726 require_noerr_quiet( err, exit );
20727
20728 ok = SCPreferencesApplyChanges( prefs );
20729 err = map_scerror( ok );
20730 require_noerr_quiet( err, exit );
20731
20732 exit:
20733 CFReleaseNullSafe( prefs );
20734 return( err );
20735 }
20736
20737 //===========================================================================================================================
20738 // _SetComputerNameWithUTF8CString
20739 //===========================================================================================================================
20740
20741 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName )
20742 {
20743 OSStatus err;
20744 CFStringRef computerName;
20745
20746 computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
20747 require_action( computerName, exit, err = kNoMemoryErr );
20748
20749 err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
20750 require_noerr_quiet( err, exit );
20751
20752 exit:
20753 CFReleaseNullSafe( computerName );
20754 return( err );
20755 }
20756
20757 //===========================================================================================================================
20758 // _SetLocalHostName
20759 //===========================================================================================================================
20760
20761 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName )
20762 {
20763 OSStatus err;
20764 SCPreferencesRef prefs;
20765 Boolean ok;
20766
20767 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20768 err = map_scerror( prefs );
20769 require_noerr_quiet( err, exit );
20770
20771 ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
20772 err = map_scerror( ok );
20773 require_noerr_quiet( err, exit );
20774
20775 ok = SCPreferencesCommitChanges( prefs );
20776 err = map_scerror( ok );
20777 require_noerr_quiet( err, exit );
20778
20779 ok = SCPreferencesApplyChanges( prefs );
20780 err = map_scerror( ok );
20781 require_noerr_quiet( err, exit );
20782
20783 exit:
20784 CFReleaseNullSafe( prefs );
20785 return( err );
20786 }
20787
20788 //===========================================================================================================================
20789 // _SetLocalHostNameWithUTF8CString
20790 //===========================================================================================================================
20791
20792 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
20793 {
20794 OSStatus err;
20795 CFStringRef localHostName;
20796
20797 localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
20798 require_action( localHostName, exit, err = kNoMemoryErr );
20799
20800 err = _SetLocalHostName( localHostName );
20801 require_noerr_quiet( err, exit );
20802
20803 exit:
20804 CFReleaseNullSafe( localHostName );
20805 return( err );
20806 }
20807
20808 //===========================================================================================================================
20809 // MDNSColliderCreate
20810 //===========================================================================================================================
20811
20812 typedef enum
20813 {
20814 kMDNSColliderOpCode_Invalid = 0,
20815 kMDNSColliderOpCode_Send = 1,
20816 kMDNSColliderOpCode_Wait = 2,
20817 kMDNSColliderOpCode_SetProbeActions = 3,
20818 kMDNSColliderOpCode_LoopPush = 4,
20819 kMDNSColliderOpCode_LoopPop = 5,
20820 kMDNSColliderOpCode_Exit = 6
20821
20822 } MDNSColliderOpCode;
20823
20824 typedef struct
20825 {
20826 MDNSColliderOpCode opcode;
20827 uint32_t operand;
20828
20829 } MDNSCInstruction;
20830
20831 #define kMaxLoopDepth 16
20832
20833 struct MDNSColliderPrivate
20834 {
20835 CFRuntimeBase base; // CF object base.
20836 dispatch_queue_t queue; // Queue for collider's events.
20837 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
20838 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
20839 SocketRef sockV4; // IPv4 UDP socket for mDNS.
20840 SocketRef sockV6; // IPv6 UDP socket for mDNS.
20841 uint8_t * target; // Record name being targeted. (malloced)
20842 uint8_t * responsePtr; // Response message pointer. (malloced)
20843 size_t responseLen; // Response message length.
20844 uint8_t * probePtr; // Probe query message pointer. (malloced)
20845 size_t probeLen; // Probe query message length.
20846 unsigned int probeCount; // Count of probe queries received for collider's record.
20847 uint32_t probeActionMap; // Bitmap of actions to take for
20848 MDNSCInstruction * program; // Program to execute.
20849 uint32_t pc; // Program's program counter.
20850 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
20851 uint32_t loopDepth; // Current loop depth.
20852 dispatch_source_t waitTimer; // Timer for program's wait commands.
20853 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
20854 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
20855 void * stopContext; // User's stop handler context.
20856 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
20857 Boolean stopped; // True if the collider has been stopped.
20858 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
20859 };
20860
20861 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
20862 static void _MDNSColliderReadHandler( void *inContext );
20863 static void _MDNSColliderExecuteProgram( void *inContext );
20864 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20865 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20866
20867 CF_CLASS_DEFINE( MDNSCollider );
20868
20869 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
20870 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20871
20872 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
20873 {
20874 OSStatus err;
20875 MDNSColliderRef obj = NULL;
20876
20877 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
20878
20879 ReplaceDispatchQueue( &obj->queue, inQueue );
20880 obj->sockV4 = kInvalidSocketRef;
20881 obj->sockV6 = kInvalidSocketRef;
20882
20883 *outCollider = obj;
20884 err = kNoErr;
20885
20886 exit:
20887 return( err );
20888 }
20889
20890 //===========================================================================================================================
20891 // _MDNSColliderFinalize
20892 //===========================================================================================================================
20893
20894 static void _MDNSColliderFinalize( CFTypeRef inObj )
20895 {
20896 MDNSColliderRef const me = (MDNSColliderRef) inObj;
20897
20898 check( !me->waitTimer );
20899 check( !me->readSourceV4 );
20900 check( !me->readSourceV6 );
20901 check( !IsValidSocket( me->sockV4 ) );
20902 check( !IsValidSocket( me->sockV6 ) );
20903 ForgetMem( &me->target );
20904 ForgetMem( &me->responsePtr );
20905 ForgetMem( &me->probePtr );
20906 ForgetMem( &me->program );
20907 dispatch_forget( &me->queue );
20908 }
20909
20910 //===========================================================================================================================
20911 // MDNSColliderStart
20912 //===========================================================================================================================
20913
20914 static void _MDNSColliderStart( void *inContext );
20915
20916 static OSStatus MDNSColliderStart( MDNSColliderRef me )
20917 {
20918 OSStatus err;
20919
20920 require_action_quiet( me->target, exit, err = kNotPreparedErr );
20921 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
20922 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
20923 require_action_quiet( me->program, exit, err = kNotPreparedErr );
20924 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
20925 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
20926
20927 CFRetain( me );
20928 dispatch_async_f( me->queue, me, _MDNSColliderStart );
20929 err = kNoErr;
20930
20931 exit:
20932 return( err );
20933 }
20934
20935 static void _MDNSColliderStart( void *inContext )
20936 {
20937 OSStatus err;
20938 MDNSColliderRef const me = (MDNSColliderRef) inContext;
20939 SocketRef sock = kInvalidSocketRef;
20940 SocketContext * sockCtx = NULL;
20941
20942 if( me->protocols & kMDNSColliderProtocol_IPv4 )
20943 {
20944 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20945 require_noerr( err, exit );
20946
20947 err = SocketContextCreate( sock, me, &sockCtx );
20948 require_noerr( err, exit );
20949 sock = kInvalidSocketRef;
20950
20951 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
20952 sockCtx, &me->readSourceV4 );
20953 require_noerr( err, exit );
20954 me->sockV4 = sockCtx->sock;
20955 sockCtx = NULL;
20956
20957 dispatch_resume( me->readSourceV4 );
20958 }
20959
20960 if( me->protocols & kMDNSColliderProtocol_IPv6 )
20961 {
20962 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20963 require_noerr( err, exit );
20964
20965 err = SocketContextCreate( sock, me, &sockCtx );
20966 require_noerr( err, exit );
20967 sock = kInvalidSocketRef;
20968
20969 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
20970 sockCtx, &me->readSourceV6 );
20971 require_noerr( err, exit );
20972 me->sockV6 = sockCtx->sock;
20973 sockCtx = NULL;
20974
20975 dispatch_resume( me->readSourceV6 );
20976 }
20977
20978 _MDNSColliderExecuteProgram( me );
20979 err = kNoErr;
20980
20981 exit:
20982 ForgetSocket( &sock );
20983 ForgetSocketContext( &sockCtx );
20984 if( err ) _MDNSColliderStop( me, err );
20985 }
20986
20987 //===========================================================================================================================
20988 // MDNSColliderStop
20989 //===========================================================================================================================
20990
20991 static void _MDNSColliderUserStop( void *inContext );
20992
20993 static void MDNSColliderStop( MDNSColliderRef me )
20994 {
20995 CFRetain( me );
20996 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
20997 }
20998
20999 static void _MDNSColliderUserStop( void *inContext )
21000 {
21001 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21002
21003 _MDNSColliderStop( me, kCanceledErr );
21004 CFRelease( me );
21005 }
21006
21007 //===========================================================================================================================
21008 // MDNSColliderSetProtocols
21009 //===========================================================================================================================
21010
21011 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
21012 {
21013 me->protocols = inProtocols;
21014 }
21015
21016 //===========================================================================================================================
21017 // MDNSColliderSetInterfaceIndex
21018 //===========================================================================================================================
21019
21020 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
21021 {
21022 me->interfaceIndex = inInterfaceIndex;
21023 }
21024
21025 //===========================================================================================================================
21026 // MDNSColliderSetProgram
21027 //===========================================================================================================================
21028
21029 #define kMDNSColliderProgCmd_Done "done"
21030 #define kMDNSColliderProgCmd_Loop "loop"
21031 #define kMDNSColliderProgCmd_Send "send"
21032 #define kMDNSColliderProgCmd_Probes "probes"
21033 #define kMDNSColliderProgCmd_Wait "wait"
21034
21035 typedef uint32_t MDNSColliderProbeAction;
21036
21037 #define kMDNSColliderProbeAction_None 0
21038 #define kMDNSColliderProbeAction_Respond 1
21039 #define kMDNSColliderProbeAction_RespondUnicast 2
21040 #define kMDNSColliderProbeAction_RespondMulticast 3
21041 #define kMDNSColliderProbeAction_Probe 4
21042 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21043
21044 #define kMDNSColliderProbeActionBits_Count 3
21045 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21046 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21047
21048 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
21049
21050 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
21051
21052 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
21053 {
21054 OSStatus err;
21055 uint32_t insCount;
21056 unsigned int loopDepth;
21057 const char * cmd;
21058 const char * end;
21059 const char * next;
21060 MDNSCInstruction * program = NULL;
21061 uint32_t loopStart[ kMaxLoopDepth ];
21062
21063 insCount = 0;
21064 for( cmd = inProgramStr; *cmd; cmd = next )
21065 {
21066 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21067 require_action_quiet( end != cmd, exit, err = kMalformedErr );
21068 next = ( *end == ';' ) ? ( end + 1 ) : end;
21069 ++insCount;
21070 }
21071
21072 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
21073 require_action( program, exit, err = kNoMemoryErr );
21074
21075 insCount = 0;
21076 loopDepth = 0;
21077 for( cmd = inProgramStr; *cmd; cmd = next )
21078 {
21079 size_t cmdLen;
21080 const char * ptr;
21081 const char * arg;
21082 size_t argLen;
21083 uint32_t value;
21084 MDNSCInstruction * const ins = &program[ insCount ];
21085
21086 while( isspace_safe( *cmd ) ) ++cmd;
21087 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21088 next = ( *end == ';' ) ? ( end + 1 ) : end;
21089
21090 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21091 cmdLen = (size_t)( ptr - cmd );
21092
21093 // Done statement
21094
21095 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
21096 {
21097 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21098 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21099
21100 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
21101
21102 ins->opcode = kMDNSColliderOpCode_LoopPop;
21103 ins->operand = loopStart[ --loopDepth ];
21104 }
21105
21106 // Loop command
21107
21108 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
21109 {
21110 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21111 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21112 require_noerr_quiet( err, exit );
21113 require_action_quiet( value > 0, exit, err = kValueErr );
21114
21115 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21116 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21117
21118 ins->opcode = kMDNSColliderOpCode_LoopPush;
21119 ins->operand = value;
21120
21121 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
21122 loopStart[ loopDepth++ ] = insCount + 1;
21123 }
21124
21125 // Probes command
21126
21127 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
21128 {
21129 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21130 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21131 argLen = (size_t)( ptr - arg );
21132 if( argLen > 0 )
21133 {
21134 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
21135 require_noerr_quiet( err, exit );
21136 }
21137 else
21138 {
21139 value = 0;
21140 }
21141
21142 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21143 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21144
21145 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
21146 ins->operand = value;
21147 }
21148
21149 // Send command
21150
21151 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
21152 {
21153 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21154 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21155
21156 ins->opcode = kMDNSColliderOpCode_Send;
21157 }
21158
21159 // Wait command
21160
21161 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
21162 {
21163 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21164 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21165 require_noerr_quiet( err, exit );
21166
21167 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21168 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21169
21170 ins->opcode = kMDNSColliderOpCode_Wait;
21171 ins->operand = value;
21172 }
21173
21174 // Unrecognized command
21175
21176 else
21177 {
21178 err = kCommandErr;
21179 goto exit;
21180 }
21181 ++insCount;
21182 }
21183 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
21184
21185 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
21186
21187 FreeNullSafe( me->program );
21188 me->program = program;
21189 program = NULL;
21190 err = kNoErr;
21191
21192 exit:
21193 FreeNullSafe( program );
21194 return( err );
21195 }
21196
21197 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
21198 {
21199 OSStatus err;
21200 const char * ptr;
21201 const char * const end = &inString[ inLen ];
21202 uint32_t bitmap;
21203 int index;
21204
21205 bitmap = 0;
21206 index = 0;
21207 ptr = inString;
21208 while( ptr < end )
21209 {
21210 int c, count;
21211 MDNSColliderProbeAction action;
21212
21213 c = *ptr++;
21214 if( isdigit_safe( c ) )
21215 {
21216 count = 0;
21217 do
21218 {
21219 count = ( count * 10 ) + ( c - '0' );
21220 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
21221 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
21222 c = *ptr++;
21223
21224 } while( isdigit_safe( c ) );
21225 require_action_quiet( count > 0, exit, err = kCountErr );
21226 }
21227 else
21228 {
21229 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
21230 count = 1;
21231 }
21232
21233 switch( c )
21234 {
21235 case 'n': action = kMDNSColliderProbeAction_None; break;
21236 case 'r': action = kMDNSColliderProbeAction_Respond; break;
21237 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
21238 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
21239 case 'p': action = kMDNSColliderProbeAction_Probe; break;
21240 default: err = kMalformedErr; goto exit;
21241 }
21242 if( ptr < end )
21243 {
21244 c = *ptr++;
21245 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
21246 }
21247 while( count-- > 0 )
21248 {
21249 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
21250 ++index;
21251 }
21252 }
21253
21254 *outBitmap = bitmap;
21255 err = kNoErr;
21256
21257 exit:
21258 return( err );
21259 }
21260
21261 //===========================================================================================================================
21262 // MDNSColliderSetStopHandler
21263 //===========================================================================================================================
21264
21265 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
21266 {
21267 me->stopHandler = inStopHandler;
21268 me->stopContext = inStopContext;
21269 }
21270
21271 //===========================================================================================================================
21272 // MDNSColliderSetRecord
21273 //===========================================================================================================================
21274
21275 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21276 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21277 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21278
21279 static OSStatus
21280 MDNSColliderSetRecord(
21281 MDNSColliderRef me,
21282 const uint8_t * inName,
21283 uint16_t inType,
21284 const void * inRDataPtr,
21285 size_t inRDataLen )
21286 {
21287 OSStatus err;
21288 DataBuffer msgDB;
21289 DNSHeader header;
21290 uint8_t * targetPtr = NULL;
21291 size_t targetLen;
21292 uint8_t * responsePtr = NULL;
21293 size_t responseLen;
21294 uint8_t * probePtr = NULL;
21295 size_t probeLen;
21296
21297 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
21298
21299 err = DomainNameDup( inName, &targetPtr, &targetLen );
21300 require_noerr_quiet( err, exit );
21301
21302 // Create response message.
21303
21304 memset( &header, 0, sizeof( header ) );
21305 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
21306 DNSHeaderSetAnswerCount( &header, 1 );
21307
21308 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21309 require_noerr( err, exit );
21310
21311 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kRRClassCacheFlushBit,
21312 1976, inRDataPtr, inRDataLen );
21313 require_noerr( err, exit );
21314
21315 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
21316 require_noerr( err, exit );
21317
21318 // Create probe message.
21319
21320 memset( &header, 0, sizeof( header ) );
21321 DNSHeaderSetQuestionCount( &header, 2 );
21322 DNSHeaderSetAuthorityCount( &header, 1 );
21323
21324 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21325 require_noerr( err, exit );
21326
21327 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
21328 require_noerr( err, exit );
21329
21330 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
21331 kDNSServiceType_NULL, kDNSServiceClass_IN );
21332 require_noerr( err, exit );
21333
21334 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
21335 1976, inRDataPtr, inRDataLen );
21336 require_noerr( err, exit );
21337
21338 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
21339 require_noerr( err, exit );
21340
21341 FreeNullSafe( me->target );
21342 me->target = targetPtr;
21343 targetPtr = NULL;
21344
21345 FreeNullSafe( me->responsePtr );
21346 me->responsePtr = responsePtr;
21347 me->responseLen = responseLen;
21348 responsePtr = NULL;
21349
21350 FreeNullSafe( me->probePtr );
21351 me->probePtr = probePtr;
21352 me->probeLen = probeLen;
21353 probePtr = NULL;
21354
21355 exit:
21356 DataBuffer_Free( &msgDB );
21357 FreeNullSafe( targetPtr );
21358 FreeNullSafe( responsePtr );
21359 FreeNullSafe( probePtr );
21360 return( err );
21361 }
21362
21363 //===========================================================================================================================
21364 // _MDNSColliderStop
21365 //===========================================================================================================================
21366
21367 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
21368 {
21369 dispatch_source_forget( &me->waitTimer );
21370 dispatch_source_forget( &me->readSourceV4 );
21371 dispatch_source_forget( &me->readSourceV6 );
21372 me->sockV4 = kInvalidSocketRef;
21373 me->sockV6 = kInvalidSocketRef;
21374
21375 if( !me->stopped )
21376 {
21377 me->stopped = true;
21378 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
21379 CFRelease( me );
21380 }
21381 }
21382
21383 //===========================================================================================================================
21384 // _MDNSColliderReadHandler
21385 //===========================================================================================================================
21386
21387 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
21388 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
21389
21390 static void _MDNSColliderReadHandler( void *inContext )
21391 {
21392 OSStatus err;
21393 struct timeval now;
21394 SocketContext * const sockCtx = (SocketContext *) inContext;
21395 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
21396 size_t msgLen;
21397 sockaddr_ip sender;
21398 const DNSHeader * hdr;
21399 const uint8_t * ptr;
21400 const struct sockaddr * dest;
21401 int probeFound, probeIsQU;
21402 unsigned int qCount, i;
21403 MDNSColliderProbeAction action;
21404
21405 gettimeofday( &now, NULL );
21406
21407 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
21408 NULL, NULL, NULL, NULL );
21409 require_noerr( err, exit );
21410
21411 require_quiet( msgLen >= kDNSHeaderLength, exit );
21412 hdr = (const DNSHeader *) me->msgBuf;
21413
21414 probeFound = false;
21415 probeIsQU = false;
21416 qCount = DNSHeaderGetQuestionCount( hdr );
21417 ptr = (const uint8_t *) &hdr[ 1 ];
21418 for( i = 0; i < qCount; ++i )
21419 {
21420 uint16_t qtype, qclass;
21421 uint8_t qname[ kDomainNameLengthMax ];
21422
21423 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
21424 require_noerr_quiet( err, exit );
21425
21426 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
21427 DomainNameEqual( qname, kMDNSColliderDummyName ) )
21428 {
21429 probeFound = false;
21430 break;
21431 }
21432
21433 if( qtype != kDNSServiceType_ANY ) continue;
21434 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
21435 if( !DomainNameEqual( qname, me->target ) ) continue;
21436
21437 if( !probeFound )
21438 {
21439 probeFound = true;
21440 probeIsQU = ( qclass & kQClassUnicastResponseBit ) ? true : false;
21441 }
21442 }
21443 require_quiet( probeFound, exit );
21444
21445 ++me->probeCount;
21446 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
21447
21448 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21449 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
21450
21451 if( ( action == kMDNSColliderProbeAction_Respond ) ||
21452 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
21453 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21454 {
21455 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
21456 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
21457 {
21458 dest = &sender.sa;
21459 }
21460 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
21461 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21462 {
21463 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21464 }
21465
21466 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
21467 require_noerr( err, exit );
21468 }
21469 else if( action == kMDNSColliderProbeAction_Probe )
21470 {
21471 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21472
21473 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
21474 require_noerr( err, exit );
21475 }
21476
21477 exit:
21478 return;
21479 }
21480
21481 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
21482 {
21483 MDNSColliderProbeAction action;
21484
21485 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
21486 {
21487 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
21488 kMDNSColliderProbeActionBits_Mask;
21489 }
21490 else
21491 {
21492 action = kMDNSColliderProbeAction_None;
21493 }
21494 return( action );
21495 }
21496
21497 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
21498 {
21499 switch( inAction )
21500 {
21501 case kMDNSColliderProbeAction_None: return( "None" );
21502 case kMDNSColliderProbeAction_Respond: return( "Respond" );
21503 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
21504 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
21505 case kMDNSColliderProbeAction_Probe: return( "Probe" );
21506 default: return( "???" );
21507 }
21508 }
21509
21510 //===========================================================================================================================
21511 // _MDNSColliderExecuteProgram
21512 //===========================================================================================================================
21513
21514 static void _MDNSColliderExecuteProgram( void *inContext )
21515 {
21516 OSStatus err;
21517 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21518 int stop;
21519
21520 dispatch_forget( &me->waitTimer );
21521
21522 stop = false;
21523 for( ;; )
21524 {
21525 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
21526 uint32_t waitMs;
21527
21528 switch( ins->opcode )
21529 {
21530 case kMDNSColliderOpCode_Send:
21531 if( IsValidSocket( me->sockV4 ) )
21532 {
21533 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
21534 require_noerr( err, exit );
21535 }
21536 if( IsValidSocket( me->sockV6 ) )
21537 {
21538 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
21539 require_noerr( err, exit );
21540 }
21541 break;
21542
21543 case kMDNSColliderOpCode_Wait:
21544 waitMs = ins->operand;
21545 if( waitMs > 0 )
21546 {
21547 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
21548 _MDNSColliderExecuteProgram, me, &me->waitTimer );
21549 require_noerr( err, exit );
21550 dispatch_resume( me->waitTimer );
21551 goto exit;
21552 }
21553 break;
21554
21555 case kMDNSColliderOpCode_SetProbeActions:
21556 me->probeCount = 0;
21557 me->probeActionMap = ins->operand;
21558 break;
21559
21560 case kMDNSColliderOpCode_LoopPush:
21561 check( me->loopDepth < kMaxLoopDepth );
21562 me->loopCounts[ me->loopDepth++ ] = ins->operand;
21563 break;
21564
21565 case kMDNSColliderOpCode_LoopPop:
21566 check( me->loopDepth > 0 );
21567 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
21568 {
21569 me->pc = ins->operand;
21570 }
21571 else
21572 {
21573 --me->loopDepth;
21574 }
21575 break;
21576
21577 case kMDNSColliderOpCode_Exit:
21578 stop = true;
21579 err = kNoErr;
21580 goto exit;
21581
21582 default:
21583 dlogassert( "Unhandled opcode %u\n", ins->opcode );
21584 err = kCommandErr;
21585 goto exit;
21586 }
21587 }
21588
21589 exit:
21590 if( err || stop ) _MDNSColliderStop( me, err );
21591 }
21592
21593 //===========================================================================================================================
21594 // _MDNSColliderSendResponse
21595 //===========================================================================================================================
21596
21597 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21598 {
21599 OSStatus err;
21600 ssize_t n;
21601
21602 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
21603 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
21604 return( err );
21605 }
21606
21607 //===========================================================================================================================
21608 // _MDNSColliderSendProbe
21609 //===========================================================================================================================
21610
21611 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21612 {
21613 OSStatus err;
21614 ssize_t n;
21615
21616 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
21617 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
21618 return( err );
21619 }
21620
21621 //===========================================================================================================================
21622 // ServiceBrowserCreate
21623 //===========================================================================================================================
21624
21625 typedef struct SBDomain SBDomain;
21626 typedef struct SBServiceType SBServiceType;
21627 typedef struct SBServiceBrowse SBServiceBrowse;
21628 typedef struct SBServiceInstance SBServiceInstance;
21629 typedef struct SBIPAddress SBIPAddress;
21630
21631 struct ServiceBrowserPrivate
21632 {
21633 CFRuntimeBase base; // CF object base.
21634 dispatch_queue_t queue; // Queue for service browser's events.
21635 DNSServiceRef connection; // Shared connection for DNS-SD ops.
21636 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
21637 char * domain; // If non-null, then browsing is limited to this domain.
21638 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
21639 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
21640 void * userContext; // User's callback context.
21641 SBDomain * domainList; // List of domains and their browse results.
21642 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
21643 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
21644 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
21645 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21646 // use the "any" interface.
21647 };
21648
21649 struct SBDomain
21650 {
21651 SBDomain * next; // Next domain object in list.
21652 ServiceBrowserRef browser; // Pointer to parent service browser.
21653 char * name; // Name of the domain.
21654 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21655 SBServiceType * typeList; // List of service types to browse for in this domain.
21656 };
21657
21658 struct SBServiceType
21659 {
21660 SBServiceType * next; // Next service type object in list.
21661 char * name; // Name of the service type.
21662 SBServiceBrowse * browseList; // List of browses for this service type.
21663 };
21664
21665 struct SBServiceBrowse
21666 {
21667 SBServiceBrowse * next; // Next browse object in list.
21668 ServiceBrowserRef browser; // Pointer to parent service browser.
21669 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
21670 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
21671 uint64_t startTicks; // Value of UpTicks() when the browse op began.
21672 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
21673 };
21674
21675 struct SBServiceInstance
21676 {
21677 SBServiceInstance * next; // Next service instance object in list.
21678 ServiceBrowserRef browser; // Pointer to parent service browser.
21679 char * name; // Name of the service instance.
21680 char * fqdn; // Fully qualified domain name of service instance (for logging/debugging).
21681 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
21682 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
21683 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
21684 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
21685 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
21686 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
21687 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
21688 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
21689 size_t txtLen; // Length of service instance's TXT record data.
21690 DNSServiceRef getAddrInfo; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21691 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21692 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
21693 };
21694
21695 struct SBIPAddress
21696 {
21697 SBIPAddress * next; // Next IP address object in list.
21698 sockaddr_ip sip; // IPv4 or IPv6 address.
21699 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
21700 };
21701
21702 typedef struct
21703 {
21704 SBRDomain * domainList; // List of domains in which services were found.
21705 int32_t refCount; // This object's reference count.
21706
21707 } ServiceBrowserResultsPrivate;
21708
21709 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
21710 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
21711 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
21712 static void _ServiceBrowserTimerHandler( void *inContext );
21713 static void DNSSD_API
21714 _ServiceBrowserDomainsQueryCallback(
21715 DNSServiceRef inSDRef,
21716 DNSServiceFlags inFlags,
21717 uint32_t inInterfaceIndex,
21718 DNSServiceErrorType inError,
21719 const char * inFullName,
21720 uint16_t inType,
21721 uint16_t inClass,
21722 uint16_t inRDataLen,
21723 const void * inRDataPtr,
21724 uint32_t inTTL,
21725 void * inContext );
21726 static void DNSSD_API
21727 _ServiceBrowserServicesQueryCallback(
21728 DNSServiceRef inSDRef,
21729 DNSServiceFlags inFlags,
21730 uint32_t inInterfaceIndex,
21731 DNSServiceErrorType inError,
21732 const char * inFullName,
21733 uint16_t inType,
21734 uint16_t inClass,
21735 uint16_t inRDataLen,
21736 const void * inRDataPtr,
21737 uint32_t inTTL,
21738 void * inContext );
21739 static void DNSSD_API
21740 _ServiceBrowserBrowseCallback(
21741 DNSServiceRef inSDRef,
21742 DNSServiceFlags inFlags,
21743 uint32_t inInterfaceIndex,
21744 DNSServiceErrorType inError,
21745 const char * inName,
21746 const char * inRegType,
21747 const char * inDomain,
21748 void * inContext );
21749 static void DNSSD_API
21750 _ServiceBrowserResolveCallback(
21751 DNSServiceRef inSDRef,
21752 DNSServiceFlags inFlags,
21753 uint32_t inInterfaceIndex,
21754 DNSServiceErrorType inError,
21755 const char * inFullName,
21756 const char * inHostname,
21757 uint16_t inPort,
21758 uint16_t inTXTLen,
21759 const unsigned char * inTXTPtr,
21760 void * inContext );
21761 static void DNSSD_API
21762 _ServiceBrowserGAICallback(
21763 DNSServiceRef inSDRef,
21764 DNSServiceFlags inFlags,
21765 uint32_t inInterfaceIndex,
21766 DNSServiceErrorType inError,
21767 const char * inHostname,
21768 const struct sockaddr * inSockAddr,
21769 uint32_t inTTL,
21770 void * inContext );
21771 static OSStatus
21772 _ServiceBrowserAddServiceType(
21773 ServiceBrowserRef inBrowser,
21774 SBDomain * inDomain,
21775 const char * inName,
21776 uint32_t inIfIndex );
21777 static OSStatus
21778 _ServiceBrowserRemoveServiceType(
21779 ServiceBrowserRef inBrowser,
21780 SBDomain * inDomain,
21781 const char * inName,
21782 uint32_t inIfIndex );
21783 static OSStatus
21784 _ServiceBrowserAddServiceInstance(
21785 ServiceBrowserRef inBrowser,
21786 SBServiceBrowse * inBrowse,
21787 uint32_t inIfIndex,
21788 const char * inName,
21789 const char * inRegType,
21790 const char * inDomain,
21791 uint64_t inDiscoverTimeUs );
21792 static OSStatus
21793 _ServiceBrowserRemoveServiceInstance(
21794 ServiceBrowserRef inBrowser,
21795 SBServiceBrowse * inBrowse,
21796 const char * inName,
21797 uint32_t inIfIndex );
21798 static OSStatus
21799 _ServiceBrowserAddIPAddress(
21800 ServiceBrowserRef inBrowser,
21801 SBServiceInstance * inInstance,
21802 const struct sockaddr * inSockAddr,
21803 uint64_t inResolveTimeUs );
21804 static OSStatus
21805 _ServiceBrowserRemoveIPAddress(
21806 ServiceBrowserRef inBrowser,
21807 SBServiceInstance * inInstance,
21808 const struct sockaddr * inSockAddr );
21809 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
21810 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
21811 static void _SBDomainFree( SBDomain *inDomain );
21812 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
21813 static void _SBServiceTypeFree( SBServiceType *inType );
21814 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
21815 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
21816 static OSStatus
21817 _SBServiceInstanceCreate(
21818 const char * inName,
21819 const char * inType,
21820 const char * inDomain,
21821 uint32_t inIfIndex,
21822 uint64_t inDiscoverTimeUs,
21823 ServiceBrowserRef inBrowser,
21824 SBServiceInstance ** outInstance );
21825 static void _SBServiceInstanceFree( SBServiceInstance *inInstance );
21826 static OSStatus
21827 _SBIPAddressCreate(
21828 const struct sockaddr * inSockAddr,
21829 uint64_t inResolveTimeUs,
21830 SBIPAddress ** outIPAddress );
21831 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
21832 static void _SBIPAddressFreeList( SBIPAddress *inList );
21833 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
21834 static void _SBRDomainFree( SBRDomain *inDomain );
21835 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
21836 static void _SBRServiceTypeFree( SBRServiceType *inType );
21837 static OSStatus
21838 _SBRServiceInstanceCreate(
21839 const char * inName,
21840 uint32_t inInterfaceIndex,
21841 const char * inHostname,
21842 uint16_t inPort,
21843 const uint8_t * inTXTPtr,
21844 size_t inTXTLen,
21845 uint64_t inDiscoverTimeUs,
21846 uint64_t inResolveTimeUs,
21847 SBRServiceInstance ** outInstance );
21848 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
21849 static OSStatus
21850 _SBRIPAddressCreate(
21851 const struct sockaddr * inSockAddr,
21852 uint64_t inResolveTimeUs,
21853 SBRIPAddress ** outIPAddress );
21854 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
21855
21856 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21857
21858 CF_CLASS_DEFINE( ServiceBrowser );
21859
21860 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
21861 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21862
21863 static OSStatus
21864 ServiceBrowserCreate(
21865 dispatch_queue_t inQueue,
21866 uint32_t inInterfaceIndex,
21867 const char * inDomain,
21868 unsigned int inBrowseTimeSecs,
21869 Boolean inIncludeAWDL,
21870 ServiceBrowserRef * outBrowser )
21871 {
21872 OSStatus err;
21873 ServiceBrowserRef obj;
21874
21875 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
21876
21877 ReplaceDispatchQueue( &obj->queue, inQueue );
21878 obj->ifIndex = inInterfaceIndex;
21879 if( inDomain )
21880 {
21881 obj->domain = strdup( inDomain );
21882 require_action( obj->domain, exit, err = kNoMemoryErr );
21883 }
21884 obj->browseTimeSecs = inBrowseTimeSecs;
21885 obj->includeAWDL = inIncludeAWDL;
21886
21887 *outBrowser = obj;
21888 obj = NULL;
21889 err = kNoErr;
21890
21891 exit:
21892 CFReleaseNullSafe( obj );
21893 return( err );
21894 }
21895
21896 //===========================================================================================================================
21897 // _ServiceBrowserFinalize
21898 //===========================================================================================================================
21899
21900 static void _ServiceBrowserFinalize( CFTypeRef inObj )
21901 {
21902 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
21903 StringListItem * serviceType;
21904
21905 dispatch_forget( &me->queue );
21906 check( !me->connection );
21907 check( !me->domainsQuery );
21908 ForgetMem( &me->domain );
21909 while( ( serviceType = me->serviceTypeList ) != NULL )
21910 {
21911 me->serviceTypeList = serviceType->next;
21912 ForgetMem( &serviceType->str );
21913 free( serviceType );
21914 }
21915 check( !me->domainList );
21916 check( !me->stopTimer );
21917 }
21918
21919 //===========================================================================================================================
21920 // ServiceBrowserStart
21921 //===========================================================================================================================
21922
21923 static void _ServiceBrowserStart( void *inContext );
21924
21925 static void ServiceBrowserStart( ServiceBrowserRef me )
21926 {
21927 CFRetain( me );
21928 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
21929 }
21930
21931 static void _ServiceBrowserStart( void *inContext )
21932 {
21933 OSStatus err;
21934 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
21935
21936 err = DNSServiceCreateConnection( &me->connection );
21937 require_noerr( err, exit );
21938
21939 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
21940 require_noerr( err, exit );
21941
21942 if( me->domain )
21943 {
21944 err = _ServiceBrowserAddDomain( me, me->domain );
21945 require_noerr( err, exit );
21946 }
21947 else
21948 {
21949 DNSServiceRef sdRef;
21950 const char * const recordName = "b._dns-sd._udp.local.";
21951 const uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
21952
21953 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21954 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21955
21956 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
21957
21958 sdRef = me->connection;
21959 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
21960 kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
21961 require_noerr( err, exit );
21962
21963 me->domainsQuery = sdRef;
21964 }
21965
21966 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
21967 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
21968 require_noerr( err, exit );
21969 dispatch_resume( me->stopTimer );
21970
21971 exit:
21972 if( err ) _ServiceBrowserStop( me, err );
21973 }
21974
21975 //===========================================================================================================================
21976 // ServiceBrowserAddServiceType
21977 //===========================================================================================================================
21978
21979 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
21980 {
21981 OSStatus err;
21982 StringListItem * item;
21983 StringListItem ** itemPtr;
21984 StringListItem * newItem = NULL;
21985
21986 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
21987 {
21988 if( strcmp( item->str, inServiceType ) == 0 ) break;
21989 }
21990 if( !item )
21991 {
21992 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
21993 require_action( newItem, exit, err = kNoMemoryErr );
21994
21995 newItem->str = strdup( inServiceType );
21996 require_action( newItem->str, exit, err = kNoMemoryErr );
21997
21998 *itemPtr = newItem;
21999 newItem = NULL;
22000 }
22001 err = kNoErr;
22002
22003 exit:
22004 FreeNullSafe( newItem );
22005 return( err );
22006 }
22007
22008 //===========================================================================================================================
22009 // ServiceBrowserSetCallback
22010 //===========================================================================================================================
22011
22012 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
22013 {
22014 me->userCallback = inCallback;
22015 me->userContext = inContext;
22016 }
22017
22018 //===========================================================================================================================
22019 // ServiceBrowserResultsRetain
22020 //===========================================================================================================================
22021
22022 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
22023 {
22024 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22025
22026 atomic_add_32( &results->refCount, 1 );
22027 }
22028
22029 //===========================================================================================================================
22030 // ServiceBrowserResultsRelease
22031 //===========================================================================================================================
22032
22033 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
22034 {
22035 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22036 SBRDomain * domain;
22037
22038 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
22039 {
22040 while( ( domain = inResults->domainList ) != NULL )
22041 {
22042 inResults->domainList = domain->next;
22043 _SBRDomainFree( domain );
22044 }
22045 free( inResults );
22046 }
22047 }
22048
22049 //===========================================================================================================================
22050 // _ServiceBrowserStop
22051 //===========================================================================================================================
22052
22053 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
22054 {
22055 OSStatus err;
22056 SBDomain * d;
22057 SBServiceType * t;
22058 SBServiceBrowse * b;
22059 SBServiceInstance * i;
22060
22061 dispatch_source_forget( &me->stopTimer );
22062 DNSServiceForget( &me->domainsQuery );
22063 for( d = me->domainList; d; d = d->next )
22064 {
22065 DNSServiceForget( &d->servicesQuery );
22066 for( t = d->typeList; t; t = t->next )
22067 {
22068 for( b = t->browseList; b; b = b->next )
22069 {
22070 DNSServiceForget( &b->browse );
22071 for( i = b->instanceList; i; i = i->next )
22072 {
22073 DNSServiceForget( &i->resolve );
22074 DNSServiceForget( &i->getAddrInfo );
22075 }
22076 }
22077 }
22078 }
22079 DNSServiceForget( &me->connection );
22080
22081 if( me->userCallback )
22082 {
22083 ServiceBrowserResults * results = NULL;
22084
22085 err = _ServiceBrowserCreateResults( me, &results );
22086 if( !err ) err = inError;
22087
22088 me->userCallback( results, err, me->userContext );
22089 me->userCallback = NULL;
22090 me->userContext = NULL;
22091 if( results ) ServiceBrowserResultsRelease( results );
22092 }
22093
22094 while( ( d = me->domainList ) != NULL )
22095 {
22096 me->domainList = d->next;
22097 _SBDomainFree( d );
22098 }
22099 CFRelease( me );
22100 }
22101
22102 //===========================================================================================================================
22103 // _ServiceBrowserAddDomain
22104 //===========================================================================================================================
22105
22106 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
22107 {
22108 OSStatus err;
22109 SBDomain * domain;
22110 SBDomain ** domainPtr;
22111 SBDomain * newDomain = NULL;
22112
22113 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22114 {
22115 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
22116 }
22117 require_action_quiet( !domain, exit, err = kDuplicateErr );
22118
22119 err = _SBDomainCreate( inDomain, me, &newDomain );
22120 require_noerr_quiet( err, exit );
22121
22122 if( me->serviceTypeList )
22123 {
22124 const StringListItem * item;
22125
22126 for( item = me->serviceTypeList; item; item = item->next )
22127 {
22128 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
22129 if( err == kDuplicateErr ) err = kNoErr;
22130 require_noerr( err, exit );
22131 }
22132 }
22133 else
22134 {
22135 char * recordName;
22136 DNSServiceRef sdRef;
22137 DNSServiceFlags flags;
22138
22139 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22140 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22141
22142 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
22143 require_action( recordName, exit, err = kNoMemoryErr );
22144
22145 flags = kDNSServiceFlagsShareConnection;
22146 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22147
22148 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
22149
22150 sdRef = newDomain->browser->connection;
22151 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
22152 _ServiceBrowserServicesQueryCallback, newDomain );
22153 free( recordName );
22154 require_noerr( err, exit );
22155
22156 newDomain->servicesQuery = sdRef;
22157 }
22158
22159 *domainPtr = newDomain;
22160 newDomain = NULL;
22161 err = kNoErr;
22162
22163 exit:
22164 if( newDomain ) _SBDomainFree( newDomain );
22165 return( err );
22166 }
22167
22168 //===========================================================================================================================
22169 // _ServiceBrowserRemoveDomain
22170 //===========================================================================================================================
22171
22172 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
22173 {
22174 OSStatus err;
22175 SBDomain * domain;
22176 SBDomain ** domainPtr;
22177
22178 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22179 {
22180 if( strcasecmp( domain->name, inName ) == 0 ) break;
22181 }
22182
22183 if( domain )
22184 {
22185 *domainPtr = domain->next;
22186 _SBDomainFree( domain );
22187 err = kNoErr;
22188 }
22189 else
22190 {
22191 err = kNotFoundErr;
22192 }
22193
22194 return( err );
22195 }
22196
22197 //===========================================================================================================================
22198 // _ServiceBrowserTimerHandler
22199 //===========================================================================================================================
22200
22201 static void _ServiceBrowserTimerHandler( void *inContext )
22202 {
22203 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22204
22205 _ServiceBrowserStop( me, kNoErr );
22206 }
22207
22208 //===========================================================================================================================
22209 // _ServiceBrowserDomainsQueryCallback
22210 //===========================================================================================================================
22211
22212 static void DNSSD_API
22213 _ServiceBrowserDomainsQueryCallback(
22214 DNSServiceRef inSDRef,
22215 DNSServiceFlags inFlags,
22216 uint32_t inInterfaceIndex,
22217 DNSServiceErrorType inError,
22218 const char * inFullName,
22219 uint16_t inType,
22220 uint16_t inClass,
22221 uint16_t inRDataLen,
22222 const void * inRDataPtr,
22223 uint32_t inTTL,
22224 void * inContext )
22225 {
22226 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22227 OSStatus err;
22228 char domainStr[ kDNSServiceMaxDomainName ];
22229
22230 Unused( inSDRef );
22231 Unused( inClass );
22232 Unused( inTTL );
22233
22234 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22235 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22236 !inError, inError );
22237
22238 require_noerr( inError, exit );
22239
22240 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
22241 require_noerr( err, exit );
22242
22243 if( inFlags & kDNSServiceFlagsAdd )
22244 {
22245 err = _ServiceBrowserAddDomain( me, domainStr );
22246 if( err == kDuplicateErr ) err = kNoErr;
22247 require_noerr( err, exit );
22248 }
22249 else
22250 {
22251 err = _ServiceBrowserRemoveDomain( me, domainStr );
22252 if( err == kNotFoundErr ) err = kNoErr;
22253 require_noerr( err, exit );
22254 }
22255
22256 exit:
22257 return;
22258 }
22259
22260 //===========================================================================================================================
22261 // _ServiceBrowserServicesQueryCallback
22262 //===========================================================================================================================
22263
22264 static void DNSSD_API
22265 _ServiceBrowserServicesQueryCallback(
22266 DNSServiceRef inSDRef,
22267 DNSServiceFlags inFlags,
22268 uint32_t inInterfaceIndex,
22269 DNSServiceErrorType inError,
22270 const char * inFullName,
22271 uint16_t inType,
22272 uint16_t inClass,
22273 uint16_t inRDataLen,
22274 const void * inRDataPtr,
22275 uint32_t inTTL,
22276 void * inContext )
22277 {
22278 OSStatus err;
22279 SBDomain * const domain = (SBDomain *) inContext;
22280 ServiceBrowserRef const me = domain->browser;
22281 const uint8_t * src;
22282 const uint8_t * end;
22283 uint8_t * dst;
22284 int i;
22285 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
22286 char serviceTypeStr[ kDNSServiceMaxDomainName ];
22287
22288 Unused( inSDRef );
22289 Unused( inTTL );
22290 Unused( inClass );
22291
22292 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22293 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22294 !inError, inError );
22295
22296 require_noerr( inError, exit );
22297
22298 check( inType == kDNSServiceType_PTR );
22299 check( inClass == kDNSServiceClass_IN );
22300
22301 // The first two labels of the domain name in the RDATA describe a service type.
22302 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22303
22304 src = (const uint8_t *) inRDataPtr;
22305 end = src + inRDataLen;
22306 dst = serviceType;
22307 for( i = 0; i < 2; ++i )
22308 {
22309 size_t labelLen;
22310
22311 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
22312
22313 labelLen = *src;
22314 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
22315 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
22316
22317 memcpy( dst, src, 1 + labelLen );
22318 src += 1 + labelLen;
22319 dst += 1 + labelLen;
22320 }
22321 *dst = 0;
22322
22323 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
22324 require_noerr( err, exit );
22325
22326 if( inFlags & kDNSServiceFlagsAdd )
22327 {
22328 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22329 if( err == kDuplicateErr ) err = kNoErr;
22330 require_noerr( err, exit );
22331 }
22332 else
22333 {
22334 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22335 if( err == kNotFoundErr ) err = kNoErr;
22336 require_noerr( err, exit );
22337 }
22338
22339 exit:
22340 return;
22341 }
22342
22343 //===========================================================================================================================
22344 // _ServiceBrowserBrowseCallback
22345 //===========================================================================================================================
22346
22347 static void DNSSD_API
22348 _ServiceBrowserBrowseCallback(
22349 DNSServiceRef inSDRef,
22350 DNSServiceFlags inFlags,
22351 uint32_t inInterfaceIndex,
22352 DNSServiceErrorType inError,
22353 const char * inName,
22354 const char * inRegType,
22355 const char * inDomain,
22356 void * inContext )
22357 {
22358 OSStatus err;
22359 const uint64_t nowTicks = UpTicks();
22360 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
22361 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
22362
22363 Unused( inSDRef );
22364
22365 sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22366 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
22367
22368 require_noerr( inError, exit );
22369
22370 if( inFlags & kDNSServiceFlagsAdd )
22371 {
22372 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
22373 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
22374 if( err == kDuplicateErr ) err = kNoErr;
22375 require_noerr( err, exit );
22376 }
22377 else
22378 {
22379 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
22380 if( err == kNotFoundErr ) err = kNoErr;
22381 require_noerr( err, exit );
22382 }
22383
22384 exit:
22385 return;
22386 }
22387
22388 //===========================================================================================================================
22389 // _ServiceBrowserResolveCallback
22390 //===========================================================================================================================
22391
22392 static void DNSSD_API
22393 _ServiceBrowserResolveCallback(
22394 DNSServiceRef inSDRef,
22395 DNSServiceFlags inFlags,
22396 uint32_t inInterfaceIndex,
22397 DNSServiceErrorType inError,
22398 const char * inFullName,
22399 const char * inHostname,
22400 uint16_t inPort,
22401 uint16_t inTXTLen,
22402 const unsigned char * inTXTPtr,
22403 void * inContext )
22404 {
22405 OSStatus err;
22406 const uint64_t nowTicks = UpTicks();
22407 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22408 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22409
22410 Unused( inSDRef );
22411 Unused( inFlags );
22412
22413 sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22414 (int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
22415
22416 require_noerr( inError, exit );
22417
22418 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
22419 {
22420 FreeNullSafe( instance->txtPtr );
22421 instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
22422 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
22423
22424 instance->txtLen = inTXTLen;
22425 }
22426
22427 instance->port = ntohs( inPort );
22428
22429 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
22430 {
22431 DNSServiceRef sdRef;
22432
22433 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
22434
22435 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
22436 require_noerr( err, exit );
22437
22438 DNSServiceForget( &instance->getAddrInfo );
22439 ForgetSBIPAddressList( &instance->ipaddrList );
22440
22441 sb_ulog( kLogLevelTrace, "Starting GetAddrInfo on interface %d for %s",
22442 (int32_t) instance->ifIndex, instance->hostname );
22443
22444 sdRef = me->connection;
22445 instance->gaiStartTicks = UpTicks();
22446 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
22447 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback, instance );
22448 require_noerr( err, exit );
22449
22450 instance->getAddrInfo = sdRef;
22451 }
22452
22453 exit:
22454 return;
22455 }
22456
22457 //===========================================================================================================================
22458 // _ServiceBrowserGAICallback
22459 //===========================================================================================================================
22460
22461 static void DNSSD_API
22462 _ServiceBrowserGAICallback(
22463 DNSServiceRef inSDRef,
22464 DNSServiceFlags inFlags,
22465 uint32_t inInterfaceIndex,
22466 DNSServiceErrorType inError,
22467 const char * inHostname,
22468 const struct sockaddr * inSockAddr,
22469 uint32_t inTTL,
22470 void * inContext )
22471 {
22472 OSStatus err;
22473 const uint64_t nowTicks = UpTicks();
22474 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22475 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22476
22477 Unused( inSDRef );
22478 Unused( inTTL );
22479
22480 sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22481 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
22482 !inError, inError );
22483
22484 require_noerr( inError, exit );
22485
22486 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22487 {
22488 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22489 goto exit;
22490 }
22491
22492 if( inFlags & kDNSServiceFlagsAdd )
22493 {
22494 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
22495 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
22496 if( err == kDuplicateErr ) err = kNoErr;
22497 require_noerr( err, exit );
22498 }
22499 else
22500 {
22501 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
22502 if( err == kNotFoundErr ) err = kNoErr;
22503 require_noerr( err, exit );
22504 }
22505
22506 exit:
22507 return;
22508 }
22509
22510 //===========================================================================================================================
22511 // _ServiceBrowserAddServiceType
22512 //===========================================================================================================================
22513
22514 static OSStatus
22515 _ServiceBrowserAddServiceType(
22516 ServiceBrowserRef me,
22517 SBDomain * inDomain,
22518 const char * inName,
22519 uint32_t inIfIndex )
22520 {
22521 OSStatus err;
22522 SBServiceType * type;
22523 SBServiceType ** typePtr;
22524 SBServiceType * newType = NULL;
22525 SBServiceBrowse * browse;
22526 SBServiceBrowse ** browsePtr;
22527 SBServiceBrowse * newBrowse = NULL;
22528 DNSServiceRef sdRef;
22529 DNSServiceFlags flags;
22530
22531 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22532 {
22533 if( strcasecmp( type->name, inName ) == 0 ) break;
22534 }
22535 if( !type )
22536 {
22537 err = _SBServiceTypeCreate( inName, &newType );
22538 require_noerr_quiet( err, exit );
22539
22540 type = newType;
22541 }
22542
22543 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22544 {
22545 if( browse->ifIndex == inIfIndex ) break;
22546 }
22547 require_action_quiet( !browse, exit, err = kDuplicateErr );
22548
22549 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
22550 require_noerr_quiet( err, exit );
22551
22552 flags = kDNSServiceFlagsShareConnection;
22553 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22554
22555 sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
22556 (int32_t) newBrowse->ifIndex, type->name, inDomain->name );
22557
22558 sdRef = me->connection;
22559 newBrowse->startTicks = UpTicks();
22560 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
22561 newBrowse );
22562 require_noerr( err, exit );
22563
22564 newBrowse->browse = sdRef;
22565 *browsePtr = newBrowse;
22566 newBrowse = NULL;
22567
22568 if( newType )
22569 {
22570 *typePtr = newType;
22571 newType = NULL;
22572 }
22573
22574 exit:
22575 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
22576 if( newType ) _SBServiceTypeFree( newType );
22577 return( err );
22578 }
22579
22580 //===========================================================================================================================
22581 // _ServiceBrowserRemoveServiceType
22582 //===========================================================================================================================
22583
22584 static OSStatus
22585 _ServiceBrowserRemoveServiceType(
22586 ServiceBrowserRef me,
22587 SBDomain * inDomain,
22588 const char * inName,
22589 uint32_t inIfIndex )
22590 {
22591 OSStatus err;
22592 SBServiceType * type;
22593 SBServiceType ** typePtr;
22594 SBServiceBrowse * browse;
22595 SBServiceBrowse ** browsePtr;
22596
22597 Unused( me );
22598
22599 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22600 {
22601 if( strcasecmp( type->name, inName ) == 0 ) break;
22602 }
22603 require_action_quiet( type, exit, err = kNotFoundErr );
22604
22605 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22606 {
22607 if( browse->ifIndex == inIfIndex ) break;
22608 }
22609 require_action_quiet( browse, exit, err = kNotFoundErr );
22610
22611 *browsePtr = browse->next;
22612 _SBServiceBrowseFree( browse );
22613 if( !type->browseList )
22614 {
22615 *typePtr = type->next;
22616 _SBServiceTypeFree( type );
22617 }
22618 err = kNoErr;
22619
22620 exit:
22621 return( err );
22622 }
22623
22624 //===========================================================================================================================
22625 // _ServiceBrowserAddServiceInstance
22626 //===========================================================================================================================
22627
22628 static OSStatus
22629 _ServiceBrowserAddServiceInstance(
22630 ServiceBrowserRef me,
22631 SBServiceBrowse * inBrowse,
22632 uint32_t inIfIndex,
22633 const char * inName,
22634 const char * inRegType,
22635 const char * inDomain,
22636 uint64_t inDiscoverTimeUs )
22637 {
22638 OSStatus err;
22639 DNSServiceRef sdRef;
22640 SBServiceInstance * instance;
22641 SBServiceInstance ** instancePtr;
22642 SBServiceInstance * newInstance = NULL;
22643
22644 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
22645 {
22646 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22647 }
22648 require_action_quiet( !instance, exit, err = kDuplicateErr );
22649
22650 err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
22651 require_noerr_quiet( err, exit );
22652
22653 sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
22654 (int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
22655
22656 sdRef = me->connection;
22657 newInstance->resolveStartTicks = UpTicks();
22658 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
22659 _ServiceBrowserResolveCallback, newInstance );
22660 require_noerr( err, exit );
22661
22662 newInstance->resolve = sdRef;
22663 *instancePtr = newInstance;
22664 newInstance = NULL;
22665
22666 exit:
22667 if( newInstance ) _SBServiceInstanceFree( newInstance );
22668 return( err );
22669 }
22670
22671 //===========================================================================================================================
22672 // _ServiceBrowserRemoveServiceInstance
22673 //===========================================================================================================================
22674
22675 static OSStatus
22676 _ServiceBrowserRemoveServiceInstance(
22677 ServiceBrowserRef me,
22678 SBServiceBrowse * inBrowse,
22679 const char * inName,
22680 uint32_t inIfIndex )
22681 {
22682 OSStatus err;
22683 SBServiceInstance * instance;
22684 SBServiceInstance ** ptr;
22685
22686 Unused( me );
22687
22688 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
22689 {
22690 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22691 }
22692 require_action_quiet( instance, exit, err = kNotFoundErr );
22693
22694 *ptr = instance->next;
22695 _SBServiceInstanceFree( instance );
22696 err = kNoErr;
22697
22698 exit:
22699 return( err );
22700 }
22701
22702 //===========================================================================================================================
22703 // _ServiceBrowserAddIPAddress
22704 //===========================================================================================================================
22705
22706 static OSStatus
22707 _ServiceBrowserAddIPAddress(
22708 ServiceBrowserRef me,
22709 SBServiceInstance * inInstance,
22710 const struct sockaddr * inSockAddr,
22711 uint64_t inResolveTimeUs )
22712 {
22713 OSStatus err;
22714 SBIPAddress * ipaddr;
22715 SBIPAddress ** ipaddrPtr;
22716 SBIPAddress * newIPAddr = NULL;
22717
22718 Unused( me );
22719
22720 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22721 {
22722 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22723 err = kTypeErr;
22724 goto exit;
22725 }
22726
22727 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22728 {
22729 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
22730 }
22731 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
22732
22733 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
22734 require_noerr_quiet( err, exit );
22735
22736 *ipaddrPtr = newIPAddr;
22737 newIPAddr = NULL;
22738 err = kNoErr;
22739
22740 exit:
22741 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
22742 return( err );
22743 }
22744
22745 //===========================================================================================================================
22746 // _ServiceBrowserRemoveIPAddress
22747 //===========================================================================================================================
22748
22749 static OSStatus
22750 _ServiceBrowserRemoveIPAddress(
22751 ServiceBrowserRef me,
22752 SBServiceInstance * inInstance,
22753 const struct sockaddr * inSockAddr )
22754 {
22755 OSStatus err;
22756 SBIPAddress * ipaddr;
22757 SBIPAddress ** ipaddrPtr;
22758
22759 Unused( me );
22760
22761 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22762 {
22763 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
22764 }
22765 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
22766
22767 *ipaddrPtr = ipaddr->next;
22768 _SBIPAddressFree( ipaddr );
22769 err = kNoErr;
22770
22771 exit:
22772 return( err );
22773 }
22774
22775 //===========================================================================================================================
22776 // _ServiceBrowserCreateResults
22777 //===========================================================================================================================
22778
22779 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
22780 {
22781 OSStatus err;
22782 SBDomain * d;
22783 SBServiceType * t;
22784 SBServiceBrowse * b;
22785 SBServiceInstance * i;
22786 SBIPAddress * a;
22787 ServiceBrowserResultsPrivate * results;
22788 SBRDomain ** domainPtr;
22789
22790 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
22791 require_action( results, exit, err = kNoMemoryErr );
22792
22793 results->refCount = 1;
22794
22795 domainPtr = &results->domainList;
22796 for( d = me->domainList; d; d = d->next )
22797 {
22798 SBRDomain * domain;
22799 SBRServiceType ** typePtr;
22800
22801 err = _SBRDomainCreate( d->name, &domain );
22802 require_noerr_quiet( err, exit );
22803 *domainPtr = domain;
22804 domainPtr = &domain->next;
22805
22806 typePtr = &domain->typeList;
22807 for( t = d->typeList; t; t = t->next )
22808 {
22809 SBRServiceType * type;
22810 SBRServiceInstance ** instancePtr;
22811
22812 err = _SBRServiceTypeCreate( t->name, &type );
22813 require_noerr_quiet( err, exit );
22814 *typePtr = type;
22815 typePtr = &type->next;
22816
22817 instancePtr = &type->instanceList;
22818 for( b = t->browseList; b; b = b->next )
22819 {
22820 for( i = b->instanceList; i; i = i->next )
22821 {
22822 SBRServiceInstance * instance;
22823 SBRIPAddress ** ipaddrPtr;
22824
22825 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
22826 i->discoverTimeUs, i->resolveTimeUs, &instance );
22827 require_noerr_quiet( err, exit );
22828 *instancePtr = instance;
22829 instancePtr = &instance->next;
22830
22831 ipaddrPtr = &instance->ipaddrList;
22832 for( a = i->ipaddrList; a; a = a->next )
22833 {
22834 SBRIPAddress * ipaddr;
22835
22836 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
22837 require_noerr_quiet( err, exit );
22838
22839 *ipaddrPtr = ipaddr;
22840 ipaddrPtr = &ipaddr->next;
22841 }
22842 }
22843 }
22844 }
22845 }
22846
22847 *outResults = (ServiceBrowserResults *) results;
22848 results = NULL;
22849 err = kNoErr;
22850
22851 exit:
22852 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
22853 return( err );
22854 }
22855
22856 //===========================================================================================================================
22857 // _SBDomainCreate
22858 //===========================================================================================================================
22859
22860 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
22861 {
22862 OSStatus err;
22863 SBDomain * obj;
22864
22865 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
22866 require_action( obj, exit, err = kNoMemoryErr );
22867
22868 obj->name = strdup( inName );
22869 require_action( obj->name, exit, err = kNoMemoryErr );
22870
22871 obj->browser = inBrowser;
22872
22873 *outDomain = obj;
22874 obj = NULL;
22875 err = kNoErr;
22876
22877 exit:
22878 if( obj ) _SBDomainFree( obj );
22879 return( err );
22880 }
22881
22882 //===========================================================================================================================
22883 // _SBDomainFree
22884 //===========================================================================================================================
22885
22886 static void _SBDomainFree( SBDomain *inDomain )
22887 {
22888 SBServiceType * type;
22889
22890 ForgetMem( &inDomain->name );
22891 DNSServiceForget( &inDomain->servicesQuery );
22892 while( ( type = inDomain->typeList ) != NULL )
22893 {
22894 inDomain->typeList = type->next;
22895 _SBServiceTypeFree( type );
22896 }
22897 free( inDomain );
22898 }
22899
22900 //===========================================================================================================================
22901 // _SBServiceTypeCreate
22902 //===========================================================================================================================
22903
22904 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
22905 {
22906 OSStatus err;
22907 SBServiceType * obj;
22908
22909 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
22910 require_action( obj, exit, err = kNoMemoryErr );
22911
22912 obj->name = strdup( inName );
22913 require_action( obj->name, exit, err = kNoMemoryErr );
22914
22915 *outType = obj;
22916 obj = NULL;
22917 err = kNoErr;
22918
22919 exit:
22920 if( obj ) _SBServiceTypeFree( obj );
22921 return( err );
22922 }
22923
22924 //===========================================================================================================================
22925 // _SBServiceTypeFree
22926 //===========================================================================================================================
22927
22928 static void _SBServiceTypeFree( SBServiceType *inType )
22929 {
22930 SBServiceBrowse * browse;
22931
22932 ForgetMem( &inType->name );
22933 while( ( browse = inType->browseList ) != NULL )
22934 {
22935 inType->browseList = browse->next;
22936 _SBServiceBrowseFree( browse );
22937 }
22938 free( inType );
22939 }
22940
22941 //===========================================================================================================================
22942 // _SBServiceBrowseCreate
22943 //===========================================================================================================================
22944
22945 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
22946 {
22947 OSStatus err;
22948 SBServiceBrowse * obj;
22949
22950 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
22951 require_action( obj, exit, err = kNoMemoryErr );
22952
22953 obj->ifIndex = inIfIndex;
22954 obj->browser = inBrowser;
22955 *outBrowse = obj;
22956 err = kNoErr;
22957
22958 exit:
22959 return( err );
22960 }
22961
22962 //===========================================================================================================================
22963 // _SBServiceBrowseFree
22964 //===========================================================================================================================
22965
22966 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
22967 {
22968 SBServiceInstance * instance;
22969
22970 DNSServiceForget( &inBrowse->browse );
22971 while( ( instance = inBrowse->instanceList ) != NULL )
22972 {
22973 inBrowse->instanceList = instance->next;
22974 _SBServiceInstanceFree( instance );
22975 }
22976 free( inBrowse );
22977 }
22978
22979 //===========================================================================================================================
22980 // _SBServiceInstanceCreate
22981 //===========================================================================================================================
22982
22983 static OSStatus
22984 _SBServiceInstanceCreate(
22985 const char * inName,
22986 const char * inType,
22987 const char * inDomain,
22988 uint32_t inIfIndex,
22989 uint64_t inDiscoverTimeUs,
22990 ServiceBrowserRef inBrowser,
22991 SBServiceInstance ** outInstance )
22992 {
22993 OSStatus err;
22994 SBServiceInstance * obj;
22995
22996 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
22997 require_action( obj, exit, err = kNoMemoryErr );
22998
22999 obj->name = strdup( inName );
23000 require_action( obj->name, exit, err = kNoMemoryErr );
23001
23002 ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
23003 require_action( obj->fqdn, exit, err = kNoMemoryErr );
23004
23005 obj->ifIndex = inIfIndex;
23006 obj->discoverTimeUs = inDiscoverTimeUs;
23007 obj->browser = inBrowser;
23008
23009 *outInstance = obj;
23010 obj = NULL;
23011 err = kNoErr;
23012
23013 exit:
23014 if( obj ) _SBServiceInstanceFree( obj );
23015 return( err );
23016 }
23017
23018 //===========================================================================================================================
23019 // _SBServiceInstanceFree
23020 //===========================================================================================================================
23021
23022 static void _SBServiceInstanceFree( SBServiceInstance *inInstance )
23023 {
23024 ForgetMem( &inInstance->name );
23025 ForgetMem( &inInstance->fqdn );
23026 DNSServiceForget( &inInstance->resolve );
23027 ForgetMem( &inInstance->hostname );
23028 ForgetMem( &inInstance->txtPtr );
23029 DNSServiceForget( &inInstance->getAddrInfo );
23030 ForgetSBIPAddressList( &inInstance->ipaddrList );
23031 free( inInstance );
23032 }
23033
23034 //===========================================================================================================================
23035 // _SBIPAddressCreate
23036 //===========================================================================================================================
23037
23038 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
23039 {
23040 OSStatus err;
23041 SBIPAddress * obj;
23042
23043 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
23044 require_action( obj, exit, err = kNoMemoryErr );
23045
23046 SockAddrCopy( inSockAddr, &obj->sip );
23047 obj->resolveTimeUs = inResolveTimeUs;
23048
23049 *outIPAddress = obj;
23050 err = kNoErr;
23051
23052 exit:
23053 return( err );
23054 }
23055
23056 //===========================================================================================================================
23057 // _SBIPAddressFree
23058 //===========================================================================================================================
23059
23060 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
23061 {
23062 free( inIPAddress );
23063 }
23064
23065 //===========================================================================================================================
23066 // _SBIPAddressFreeList
23067 //===========================================================================================================================
23068
23069 static void _SBIPAddressFreeList( SBIPAddress *inList )
23070 {
23071 SBIPAddress * ipaddr;
23072
23073 while( ( ipaddr = inList ) != NULL )
23074 {
23075 inList = ipaddr->next;
23076 _SBIPAddressFree( ipaddr );
23077 }
23078 }
23079
23080 //===========================================================================================================================
23081 // _SBRDomainCreate
23082 //===========================================================================================================================
23083
23084 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
23085 {
23086 OSStatus err;
23087 SBRDomain * obj;
23088
23089 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
23090 require_action( obj, exit, err = kNoMemoryErr );
23091
23092 obj->name = strdup( inName );
23093 require_action( obj->name, exit, err = kNoMemoryErr );
23094
23095 *outDomain = obj;
23096 obj = NULL;
23097 err = kNoErr;
23098
23099 exit:
23100 if( obj ) _SBRDomainFree( obj );
23101 return( err );
23102 }
23103
23104 //===========================================================================================================================
23105 // _SBRDomainFree
23106 //===========================================================================================================================
23107
23108 static void _SBRDomainFree( SBRDomain *inDomain )
23109 {
23110 SBRServiceType * type;
23111
23112 ForgetMem( &inDomain->name );
23113 while( ( type = inDomain->typeList ) != NULL )
23114 {
23115 inDomain->typeList = type->next;
23116 _SBRServiceTypeFree( type );
23117 }
23118 free( inDomain );
23119 }
23120
23121 //===========================================================================================================================
23122 // _SBRServiceTypeCreate
23123 //===========================================================================================================================
23124
23125 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
23126 {
23127 OSStatus err;
23128 SBRServiceType * obj;
23129
23130 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
23131 require_action( obj, exit, err = kNoMemoryErr );
23132
23133 obj->name = strdup( inName );
23134 require_action( obj->name, exit, err = kNoMemoryErr );
23135
23136 *outType = obj;
23137 obj = NULL;
23138 err = kNoErr;
23139
23140 exit:
23141 if( obj ) _SBRServiceTypeFree( obj );
23142 return( err );
23143 }
23144
23145 //===========================================================================================================================
23146 // _SBRServiceTypeFree
23147 //===========================================================================================================================
23148
23149 static void _SBRServiceTypeFree( SBRServiceType *inType )
23150 {
23151 SBRServiceInstance * instance;
23152
23153 ForgetMem( &inType->name );
23154 while( ( instance = inType->instanceList ) != NULL )
23155 {
23156 inType->instanceList = instance->next;
23157 _SBRServiceInstanceFree( instance );
23158 }
23159 free( inType );
23160 }
23161
23162 //===========================================================================================================================
23163 // _SBRServiceInstanceCreate
23164 //===========================================================================================================================
23165
23166 static OSStatus
23167 _SBRServiceInstanceCreate(
23168 const char * inName,
23169 uint32_t inInterfaceIndex,
23170 const char * inHostname,
23171 uint16_t inPort,
23172 const uint8_t * inTXTPtr,
23173 size_t inTXTLen,
23174 uint64_t inDiscoverTimeUs,
23175 uint64_t inResolveTimeUs,
23176 SBRServiceInstance ** outInstance )
23177 {
23178 OSStatus err;
23179 SBRServiceInstance * obj;
23180
23181 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
23182 require_action( obj, exit, err = kNoMemoryErr );
23183
23184 obj->name = strdup( inName );
23185 require_action( obj->name, exit, err = kNoMemoryErr );
23186
23187 if( inHostname )
23188 {
23189 obj->hostname = strdup( inHostname );
23190 require_action( obj->hostname, exit, err = kNoMemoryErr );
23191 }
23192 if( inTXTLen > 0 )
23193 {
23194 obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
23195 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
23196 obj->txtLen = inTXTLen;
23197 }
23198 obj->discoverTimeUs = inDiscoverTimeUs;
23199 obj->resolveTimeUs = inResolveTimeUs;
23200 obj->ifIndex = inInterfaceIndex;
23201 obj->port = inPort;
23202
23203 *outInstance = obj;
23204 obj = NULL;
23205 err = kNoErr;
23206
23207 exit:
23208 if( obj ) _SBRServiceInstanceFree( obj );
23209 return( err );
23210 }
23211
23212 //===========================================================================================================================
23213 // _SBRServiceInstanceFree
23214 //===========================================================================================================================
23215
23216 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
23217 {
23218 SBRIPAddress * ipaddr;
23219
23220 ForgetMem( &inInstance->name );
23221 ForgetMem( &inInstance->hostname );
23222 ForgetMem( &inInstance->txtPtr );
23223 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
23224 {
23225 inInstance->ipaddrList = ipaddr->next;
23226 _SBRIPAddressFree( ipaddr );
23227 }
23228 free( inInstance );
23229 }
23230
23231 //===========================================================================================================================
23232 // _SBRIPAddressCreate
23233 //===========================================================================================================================
23234
23235 static OSStatus
23236 _SBRIPAddressCreate(
23237 const struct sockaddr * inSockAddr,
23238 uint64_t inResolveTimeUs,
23239 SBRIPAddress ** outIPAddress )
23240 {
23241 OSStatus err;
23242 SBRIPAddress * obj;
23243
23244 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
23245 require_action( obj, exit, err = kNoMemoryErr );
23246
23247 SockAddrCopy( inSockAddr, &obj->sip );
23248 obj->resolveTimeUs = inResolveTimeUs;
23249
23250 *outIPAddress = obj;
23251 err = kNoErr;
23252
23253 exit:
23254 return( err );
23255 }
23256
23257 //===========================================================================================================================
23258 // _SBRIPAddressFree
23259 //===========================================================================================================================
23260
23261 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
23262 {
23263 free( inIPAddress );
23264 }
23265
23266 //===========================================================================================================================
23267 // _SocketWriteAll
23268 //
23269 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23270 //===========================================================================================================================
23271
23272 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
23273 {
23274 OSStatus err;
23275 const uint8_t * src;
23276 const uint8_t * end;
23277 fd_set writeSet;
23278 struct timeval timeout;
23279 ssize_t n;
23280
23281 FD_ZERO( &writeSet );
23282 src = (const uint8_t *) inData;
23283 end = src + inSize;
23284 while( src < end )
23285 {
23286 FD_SET( inSock, &writeSet );
23287 timeout.tv_sec = inTimeoutSecs;
23288 timeout.tv_usec = 0;
23289 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
23290 if( n == 0 ) { err = kTimeoutErr; goto exit; }
23291 err = map_socket_value_errno( inSock, n > 0, n );
23292 require_noerr( err, exit );
23293
23294 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
23295 err = map_socket_value_errno( inSock, n >= 0, n );
23296 if( err == EINTR ) continue;
23297 require_noerr( err, exit );
23298
23299 src += n;
23300 }
23301 err = kNoErr;
23302
23303 exit:
23304 return( err );
23305 }
23306
23307 //===========================================================================================================================
23308 // _ParseIPv4Address
23309 //
23310 // Warning: "inBuffer" may be modified even in error cases.
23311 //
23312 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23313 //===========================================================================================================================
23314
23315 static OSStatus _ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
23316 {
23317 OSStatus err;
23318 uint8_t * dst;
23319 int segments;
23320 int sawDigit;
23321 int c;
23322 int v;
23323
23324 check( inBuffer );
23325 check( outStr );
23326
23327 dst = inBuffer;
23328 *dst = 0;
23329 sawDigit = 0;
23330 segments = 0;
23331 for( ; ( c = *inStr ) != '\0'; ++inStr )
23332 {
23333 if( isdigit_safe( c ) )
23334 {
23335 v = ( *dst * 10 ) + ( c - '0' );
23336 require_action_quiet( v <= 255, exit, err = kRangeErr );
23337 *dst = (uint8_t) v;
23338 if( !sawDigit )
23339 {
23340 ++segments;
23341 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
23342 sawDigit = 1;
23343 }
23344 }
23345 else if( ( c == '.' ) && sawDigit )
23346 {
23347 require_action_quiet( segments < 4, exit, err = kMalformedErr );
23348 *++dst = 0;
23349 sawDigit = 0;
23350 }
23351 else
23352 {
23353 break;
23354 }
23355 }
23356 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
23357
23358 *outStr = inStr;
23359 err = kNoErr;
23360
23361 exit:
23362 return( err );
23363 }
23364
23365 //===========================================================================================================================
23366 // _StringToIPv4Address
23367 //
23368 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23369 //===========================================================================================================================
23370
23371 static OSStatus
23372 _StringToIPv4Address(
23373 const char * inStr,
23374 StringToIPAddressFlags inFlags,
23375 uint32_t * outIP,
23376 int * outPort,
23377 uint32_t * outSubnet,
23378 uint32_t * outRouter,
23379 const char ** outStr )
23380 {
23381 OSStatus err;
23382 uint8_t buf[ 4 ];
23383 int c;
23384 uint32_t ip;
23385 int hasPort;
23386 int port;
23387 int hasPrefix;
23388 int prefix;
23389 uint32_t subnetMask;
23390 uint32_t router;
23391
23392 require_action( inStr, exit, err = kParamErr );
23393
23394 // Parse the address-only part of the address (e.g. "1.2.3.4").
23395
23396 err = _ParseIPv4Address( inStr, buf, &inStr );
23397 require_noerr_quiet( err, exit );
23398 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
23399 c = *inStr;
23400
23401 // Parse the port (if any).
23402
23403 hasPort = 0;
23404 port = 0;
23405 if( c == ':' )
23406 {
23407 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23408 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23409 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23410 hasPort = 1;
23411 }
23412
23413 // Parse the prefix length (if any).
23414
23415 hasPrefix = 0;
23416 prefix = 0;
23417 subnetMask = 0;
23418 router = 0;
23419 if( c == '/' )
23420 {
23421 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23422 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23423 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
23424 hasPrefix = 1;
23425
23426 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
23427 router = ( ip & subnetMask ) | 1;
23428 }
23429
23430 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23431
23432 if( outIP ) *outIP = ip;
23433 if( outPort && hasPort ) *outPort = port;
23434 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
23435 if( outRouter && hasPrefix ) *outRouter = router;
23436 if( outStr ) *outStr = inStr;
23437 err = kNoErr;
23438
23439 exit:
23440 return( err );
23441 }
23442
23443 //===========================================================================================================================
23444 // _ParseIPv6Address
23445 //
23446 // Note: Parsed according to the rules specified in RFC 3513.
23447 // Warning: "inBuffer" may be modified even in error cases.
23448 //
23449 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23450 //===========================================================================================================================
23451
23452 static OSStatus _ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
23453 {
23454 // Table to map uppercase hex characters - '0' to their numeric values.
23455 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23456 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 };
23457 OSStatus err;
23458 const char * ptr;
23459 uint8_t * dst;
23460 uint8_t * lim;
23461 uint8_t * colonPtr;
23462 int c;
23463 int sawDigit;
23464 unsigned int v;
23465 int i;
23466 int n;
23467
23468 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23469
23470 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
23471
23472 // Special case leading :: (e.g. "::1") to simplify processing later.
23473
23474 if( *inStr == ':' )
23475 {
23476 ++inStr;
23477 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
23478 }
23479
23480 // Parse the address.
23481
23482 ptr = inStr;
23483 dst = inBuffer;
23484 lim = dst + 16;
23485 colonPtr = NULL;
23486 sawDigit = 0;
23487 v = 0;
23488 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
23489 {
23490 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
23491 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
23492 {
23493 c -= '0';
23494 check( c < (int) countof( kASCIItoHexTable ) );
23495 v = ( v << 4 ) | kASCIItoHexTable[ c ];
23496 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
23497 sawDigit = 1;
23498 continue;
23499 }
23500 if( c == ':' )
23501 {
23502 ptr = inStr;
23503 if( !sawDigit )
23504 {
23505 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
23506 colonPtr = dst;
23507 continue;
23508 }
23509 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
23510 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23511 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23512 *dst++ = (uint8_t)( v & 0xFF );
23513 sawDigit = 0;
23514 v = 0;
23515 continue;
23516 }
23517
23518 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23519
23520 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
23521 {
23522 err = _ParseIPv4Address( ptr, dst, &inStr );
23523 require_noerr_quiet( err, exit );
23524 dst += 4;
23525 sawDigit = 0;
23526 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
23527 }
23528 break;
23529 }
23530 if( sawDigit )
23531 {
23532 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23533 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23534 *dst++ = (uint8_t)( v & 0xFF );
23535 }
23536 check( dst <= lim );
23537 if( colonPtr )
23538 {
23539 require_action_quiet( dst < lim, exit, err = kOverrunErr );
23540 n = (int)( dst - colonPtr );
23541 for( i = 1; i <= n; ++i )
23542 {
23543 lim[ -i ] = colonPtr[ n - i ];
23544 colonPtr[ n - i ] = 0;
23545 }
23546 dst = lim;
23547 }
23548 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
23549
23550 *outStr = inStr - 1;
23551 err = kNoErr;
23552
23553 exit:
23554 return( err );
23555 }
23556
23557 //===========================================================================================================================
23558 // _ParseIPv6Scope
23559 //
23560 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23561 //===========================================================================================================================
23562
23563 static OSStatus _ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
23564 {
23565 #if( TARGET_OS_POSIX )
23566 OSStatus err;
23567 char scopeStr[ 64 ];
23568 char * dst;
23569 char * lim;
23570 int c;
23571 uint32_t scope;
23572 const char * ptr;
23573
23574 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23575
23576 dst = scopeStr;
23577 lim = dst + ( countof( scopeStr ) - 1 );
23578 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
23579 {
23580 *dst++ = *inStr++;
23581 }
23582 *dst = '\0';
23583 check( dst <= lim );
23584
23585 // First try to map as a name and if that fails, treat it as a numeric scope.
23586
23587 scope = if_nametoindex( scopeStr );
23588 if( scope == 0 )
23589 {
23590 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
23591 {
23592 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
23593 }
23594 require_action_quiet( c == '\0', exit, err = kMalformedErr );
23595 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
23596 }
23597
23598 *outScope = scope;
23599 *outStr = inStr;
23600 err = kNoErr;
23601
23602 exit:
23603 return( err );
23604 #else
23605 OSStatus err;
23606 uint32_t scope;
23607 const char * start;
23608 int c;
23609
23610 scope = 0;
23611 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
23612 {
23613 scope = ( scope * 10 ) + ( c - '0' );
23614 }
23615 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
23616
23617 *outScope = scope;
23618 *outStr = inStr;
23619 err = kNoErr;
23620
23621 exit:
23622 return( err );
23623 #endif
23624 }
23625
23626 //===========================================================================================================================
23627 // _StringToIPv6Address
23628 //
23629 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23630 //===========================================================================================================================
23631
23632 static OSStatus
23633 _StringToIPv6Address(
23634 const char * inStr,
23635 StringToIPAddressFlags inFlags,
23636 uint8_t outIPv6[ 16 ],
23637 uint32_t * outScope,
23638 int * outPort,
23639 int * outPrefix,
23640 const char ** outStr )
23641 {
23642 OSStatus err;
23643 uint8_t ipv6[ 16 ];
23644 int c;
23645 int hasScope;
23646 uint32_t scope;
23647 int hasPort;
23648 int port;
23649 int hasPrefix;
23650 int prefix;
23651 int hasBracket;
23652 int i;
23653
23654 require_action( inStr, exit, err = kParamErr );
23655
23656 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23657
23658 // Parse the address-only part of the address (e.g. "1::1").
23659
23660 err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
23661 require_noerr_quiet( err, exit );
23662 c = *inStr;
23663
23664 // Parse the scope, port, or prefix length.
23665
23666 hasScope = 0;
23667 scope = 0;
23668 hasPort = 0;
23669 port = 0;
23670 hasPrefix = 0;
23671 prefix = 0;
23672 hasBracket = 0;
23673 for( ;; )
23674 {
23675 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
23676 {
23677 require_action_quiet( !hasScope, exit, err = kMalformedErr );
23678 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
23679 ++inStr;
23680 err = _ParseIPv6Scope( inStr, &scope, &inStr );
23681 require_noerr_quiet( err, exit );
23682 hasScope = 1;
23683 c = *inStr;
23684 }
23685 else if( c == ':' ) // Port (e.g. ":80")
23686 {
23687 require_action_quiet( !hasPort, exit, err = kMalformedErr );
23688 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23689 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23690 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23691 hasPort = 1;
23692 }
23693 else if( c == '/' ) // Prefix Length (e.g. "/64")
23694 {
23695 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
23696 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23697 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23698 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
23699 hasPrefix = 1;
23700 }
23701 else if( c == ']' )
23702 {
23703 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
23704 hasBracket = 1;
23705 c = *( ++inStr );
23706 }
23707 else
23708 {
23709 break;
23710 }
23711 }
23712
23713 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23714
23715 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
23716 if( outScope && hasScope ) *outScope = scope;
23717 if( outPort && hasPort ) *outPort = port;
23718 if( outPrefix && hasPrefix ) *outPrefix = prefix;
23719 if( outStr ) *outStr = inStr;
23720 err = kNoErr;
23721
23722 exit:
23723 return( err );
23724 }
23725
23726 //===========================================================================================================================
23727 // _StringArray_Free
23728 //
23729 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23730 //===========================================================================================================================
23731
23732 static void _StringArray_Free( char **inArray, size_t inCount )
23733 {
23734 size_t i;
23735
23736 for( i = 0; i < inCount; ++i )
23737 {
23738 free( inArray[ i ] );
23739 }
23740 if( inCount > 0 ) free( inArray );
23741 }
23742
23743 //===========================================================================================================================
23744 // _ParseQuotedEscapedString
23745 //
23746 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23747 //===========================================================================================================================
23748
23749 static Boolean
23750 _ParseQuotedEscapedString(
23751 const char * inSrc,
23752 const char * inEnd,
23753 const char * inDelimiters,
23754 char * inBuf,
23755 size_t inMaxLen,
23756 size_t * outCopiedLen,
23757 size_t * outTotalLen,
23758 const char ** outSrc )
23759 {
23760 const unsigned char * src;
23761 const unsigned char * end;
23762 unsigned char * dst;
23763 unsigned char * lim;
23764 unsigned char c;
23765 unsigned char c2;
23766 size_t totalLen;
23767 Boolean singleQuote;
23768 Boolean doubleQuote;
23769
23770 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
23771 src = (const unsigned char *) inSrc;
23772 end = (const unsigned char *) inEnd;
23773 dst = (unsigned char *) inBuf;
23774 lim = dst + inMaxLen;
23775 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
23776 if( src >= end ) return( false );
23777
23778 // Parse each argument from the string.
23779 //
23780 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23781
23782 totalLen = 0;
23783 singleQuote = false;
23784 doubleQuote = false;
23785 while( src < end )
23786 {
23787 c = *src++;
23788 if( singleQuote )
23789 {
23790 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23791
23792 if( c == '\'' )
23793 {
23794 singleQuote = false;
23795 continue;
23796 }
23797 }
23798 else if( doubleQuote )
23799 {
23800 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23801 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23802 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23803 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23804 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23805
23806 if( c == '"' )
23807 {
23808 doubleQuote = false;
23809 continue;
23810 }
23811 else if( c == '\\' )
23812 {
23813 if( src < end )
23814 {
23815 c2 = *src;
23816 if( ( c2 == '"' ) || ( c2 == '\\' ) )
23817 {
23818 ++src;
23819 c = c2;
23820 }
23821 else if( c2 == '\n' )
23822 {
23823 ++src;
23824 continue;
23825 }
23826 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
23827 {
23828 ++src;
23829 c = c2;
23830 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23831 {
23832 c = HexPairToByte( src );
23833 src += 2;
23834 }
23835 }
23836 else if( isoctal_safe( c2 ) )
23837 {
23838 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23839 {
23840 c = OctalTripleToByte( src );
23841 src += 3;
23842 }
23843 }
23844 }
23845 }
23846 }
23847 else if( strchr( inDelimiters, c ) )
23848 {
23849 break;
23850 }
23851 else if( c == '\\' )
23852 {
23853 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23854 // A backslash followed by a newline disappears completely.
23855 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23856 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23857
23858 if( src < end )
23859 {
23860 c = *src;
23861 if( c == '\n' )
23862 {
23863 ++src;
23864 continue;
23865 }
23866 else if( ( c == 'x' ) || ( c == 'X' ) )
23867 {
23868 ++src;
23869 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23870 {
23871 c = HexPairToByte( src );
23872 src += 2;
23873 }
23874 }
23875 else if( isoctal_safe( c ) )
23876 {
23877 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23878 {
23879 c = OctalTripleToByte( src );
23880 src += 3;
23881 }
23882 else
23883 {
23884 ++src;
23885 }
23886 }
23887 else
23888 {
23889 ++src;
23890 }
23891 }
23892 }
23893 else if( c == '\'' )
23894 {
23895 singleQuote = true;
23896 continue;
23897 }
23898 else if( c == '"' )
23899 {
23900 doubleQuote = true;
23901 continue;
23902 }
23903
23904 if( dst < lim )
23905 {
23906 if( inBuf ) *dst = c;
23907 ++dst;
23908 }
23909 ++totalLen;
23910 }
23911
23912 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
23913 if( outTotalLen ) *outTotalLen = totalLen;
23914 if( outSrc ) *outSrc = (const char *) src;
23915 return( true );
23916 }
23917
23918 //===========================================================================================================================
23919 // _ServerSocketOpenEx2
23920 //
23921 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23922 //===========================================================================================================================
23923
23924 static OSStatus
23925 _ServerSocketOpenEx2(
23926 int inFamily,
23927 int inType,
23928 int inProtocol,
23929 const void * inAddr,
23930 int inPort,
23931 int * outPort,
23932 int inRcvBufSize,
23933 Boolean inNoPortReuse,
23934 SocketRef * outSock )
23935 {
23936 OSStatus err;
23937 int port;
23938 SocketRef sock;
23939 int name;
23940 int option;
23941 sockaddr_ip sip;
23942 socklen_t len;
23943
23944 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
23945
23946 sock = socket( inFamily, inType, inProtocol );
23947 err = map_socket_creation_errno( sock );
23948 require_noerr_quiet( err, exit );
23949
23950 #if( defined( SO_NOSIGPIPE ) )
23951 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
23952 #endif
23953
23954 err = SocketMakeNonBlocking( sock );
23955 require_noerr( err, exit );
23956
23957 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23958 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23959 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23960 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23961
23962 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
23963 check_noerr( err );
23964
23965 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23966
23967 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
23968 {
23969 option = 1;
23970 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
23971 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
23972 err = map_socket_noerr_errno( sock, err );
23973 require_noerr( err, exit );
23974 }
23975
23976 if( inFamily == AF_INET )
23977 {
23978 // Bind to the port. If it fails, retry with a dynamic port.
23979
23980 memset( &sip.v4, 0, sizeof( sip.v4 ) );
23981 SIN_LEN_SET( &sip.v4 );
23982 sip.v4.sin_family = AF_INET;
23983 sip.v4.sin_port = htons( (uint16_t) port );
23984 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
23985 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
23986 err = map_socket_noerr_errno( sock, err );
23987 if( err && ( inPort < 0 ) )
23988 {
23989 sip.v4.sin_port = 0;
23990 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
23991 err = map_socket_noerr_errno( sock, err );
23992 }
23993 require_noerr( err, exit );
23994 }
23995 #if( defined( AF_INET6 ) )
23996 else if( inFamily == AF_INET6 )
23997 {
23998 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
23999
24000 option = 1;
24001 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
24002 err = map_socket_noerr_errno( sock, err );
24003 require_noerr( err, exit );
24004
24005 // Bind to the port. If it fails, retry with a dynamic port.
24006
24007 memset( &sip.v6, 0, sizeof( sip.v6 ) );
24008 SIN6_LEN_SET( &sip.v6 );
24009 sip.v6.sin6_family = AF_INET6;
24010 sip.v6.sin6_port = htons( (uint16_t) port );
24011 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
24012 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24013 err = map_socket_noerr_errno( sock, err );
24014 if( err && ( inPort < 0 ) )
24015 {
24016 sip.v6.sin6_port = 0;
24017 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24018 err = map_socket_noerr_errno( sock, err );
24019 }
24020 require_noerr( err, exit );
24021 }
24022 #endif
24023 else
24024 {
24025 dlogassert( "Unsupported family: %d", inFamily );
24026 err = kUnsupportedErr;
24027 goto exit;
24028 }
24029
24030 if( inType == SOCK_STREAM )
24031 {
24032 err = listen( sock, SOMAXCONN );
24033 err = map_socket_noerr_errno( sock, err );
24034 if( err )
24035 {
24036 err = listen( sock, 5 );
24037 err = map_socket_noerr_errno( sock, err );
24038 require_noerr( err, exit );
24039 }
24040 }
24041
24042 if( outPort )
24043 {
24044 len = (socklen_t) sizeof( sip );
24045 err = getsockname( sock, &sip.sa, &len );
24046 err = map_socket_noerr_errno( sock, err );
24047 require_noerr( err, exit );
24048
24049 *outPort = SockAddrGetPort( &sip );
24050 }
24051 *outSock = sock;
24052 sock = kInvalidSocketRef;
24053
24054 exit:
24055 ForgetSocket( &sock );
24056 return( err );
24057 }
24058
24059 //===========================================================================================================================
24060 // _memdup
24061 //
24062 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24063 //===========================================================================================================================
24064
24065 static void * _memdup( const void *inPtr, size_t inLen )
24066 {
24067 void * mem;
24068
24069 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24070 require( mem, exit );
24071 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
24072
24073 exit:
24074 return( mem );
24075 }
24076
24077 //===========================================================================================================================
24078 // _memicmp
24079 //
24080 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24081 //===========================================================================================================================
24082
24083 static int _memicmp( const void *inP1, const void *inP2, size_t inLen )
24084 {
24085 const unsigned char * p1;
24086 const unsigned char * e1;
24087 const unsigned char * p2;
24088 int c1;
24089 int c2;
24090
24091 p1 = (const unsigned char *) inP1;
24092 e1 = p1 + inLen;
24093 p2 = (const unsigned char *) inP2;
24094 while( p1 < e1 )
24095 {
24096 c1 = *p1++;
24097 c2 = *p2++;
24098 c1 = tolower( c1 );
24099 c2 = tolower( c2 );
24100 if( c1 < c2 ) return( -1 );
24101 if( c1 > c2 ) return( 1 );
24102 }
24103 return( 0 );
24104 }
24105
24106 //===========================================================================================================================
24107 // _FNV1
24108 //
24109 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24110 //===========================================================================================================================
24111
24112 static uint32_t _FNV1( const void *inData, size_t inSize )
24113 {
24114 const uint8_t * src = (const uint8_t *) inData;
24115 const uint8_t * const end = src + inSize;
24116 uint32_t hash;
24117
24118 hash = 0x811c9dc5U;
24119 while( src != end )
24120 {
24121 hash *= 0x01000193;
24122 hash ^= *src++;
24123 }
24124 return( hash );
24125 }