]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dnssdutil.c
mDNSResponder-1096.60.2.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / dnssdutil.c
1 /*
2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
3
4 dnssdutil is a command-line utility for testing the DNS-SD API.
5 */
6
7 #include "DNSMessage.h"
8
9 #include <CoreUtils/CoreUtils.h>
10 #include <dns_sd.h>
11 #include <dns_sd_private.h>
12
13 #include CF_RUNTIME_HEADER
14
15 #if( TARGET_OS_DARWIN )
16 #include <CFNetwork/CFHost.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <SystemConfiguration/SCPrivate.h>
19 #include <dnsinfo.h>
20 #include <libproc.h>
21 #include <netdb.h>
22 #include <pcap.h>
23 #include <spawn.h>
24 #include <sys/proc_info.h>
25 #include <xpc/xpc.h>
26 #endif
27
28 #if( TARGET_OS_POSIX )
29 #include <sys/resource.h>
30 #endif
31
32 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
33 #define DNSSDUTIL_INCLUDE_DNSCRYPT 0
34 #endif
35
36 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
37 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
38 #endif
39
40 #if( !defined( MDNSRESPONDER_PROJECT ) )
41 #define MDNSRESPONDER_PROJECT 0
42 #endif
43
44 #if( MDNSRESPONDER_PROJECT )
45 #include <dns_services.h>
46 #include "mdns_private.h"
47 #include "TestUtils.h"
48 #endif
49
50 //===========================================================================================================================
51 // Versioning
52 //===========================================================================================================================
53
54 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
55
56 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
57 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
58 #endif
59
60 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
61
62 //===========================================================================================================================
63 // DNS-SD
64 //===========================================================================================================================
65
66 // DNS-SD API flag descriptors
67
68 #define kDNSServiceFlagsDescriptors \
69 "\x00" "AutoTrigger\0" \
70 "\x01" "Add\0" \
71 "\x02" "Default\0" \
72 "\x03" "NoAutoRename\0" \
73 "\x04" "Shared\0" \
74 "\x05" "Unique\0" \
75 "\x06" "BrowseDomains\0" \
76 "\x07" "RegistrationDomains\0" \
77 "\x08" "LongLivedQuery\0" \
78 "\x09" "AllowRemoteQuery\0" \
79 "\x0A" "ForceMulticast\0" \
80 "\x0B" "KnownUnique\0" \
81 "\x0C" "ReturnIntermediates\0" \
82 "\x0D" "DenyConstrained\0" \
83 "\x0E" "ShareConnection\0" \
84 "\x0F" "SuppressUnusable\0" \
85 "\x10" "Timeout\0" \
86 "\x11" "IncludeP2P\0" \
87 "\x12" "WakeOnResolve\0" \
88 "\x13" "BackgroundTrafficClass\0" \
89 "\x14" "IncludeAWDL\0" \
90 "\x15" "Validate\0" \
91 "\x16" "UnicastResponse\0" \
92 "\x17" "ValidateOptional\0" \
93 "\x18" "WakeOnlyService\0" \
94 "\x19" "ThresholdOne\0" \
95 "\x1A" "ThresholdFinder\0" \
96 "\x1B" "DenyCellular\0" \
97 "\x1C" "ServiceIndex\0" \
98 "\x1D" "DenyExpensive\0" \
99 "\x1E" "PathEvaluationDone\0" \
100 "\x1F" "AllowExpiredAnswers\0" \
101 "\x00"
102
103 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
104
105 #define kDNSServiceProtocolDescriptors \
106 "\x00" "IPv4\0" \
107 "\x01" "IPv6\0" \
108 "\x04" "UDP\0" \
109 "\x05" "TCP\0" \
110 "\x00"
111
112 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
113
114 //===========================================================================================================================
115 // DNS
116 //===========================================================================================================================
117
118 #define kDNSPort 53
119 #define kDNSMaxUDPMessageSize 512
120 #define kDNSMaxTCPMessageSize UINT16_MAX
121
122 #define kDNSRecordDataLengthMax UINT16_MAX
123
124 //===========================================================================================================================
125 // mDNS
126 //===========================================================================================================================
127
128 #define kMDNSPort 5353
129
130 #define kDefaultMDNSMessageID 0
131 #define kDefaultMDNSQueryFlags 0
132
133 #define kQClassUnicastResponseBit ( 1U << 15 )
134 #define kRRClassCacheFlushBit ( 1U << 15 )
135
136 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
137
138 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
139 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
140
141 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
142
143 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
144
145 #define kLocalStr "\x05" "local"
146 #define kLocalLabel ( (const uint8_t *) kLocalStr )
147 #define kLocalName ( (const uint8_t *) kLocalStr )
148 #define kLocalNameLen sizeof( kLocalStr )
149
150 //===========================================================================================================================
151 // Test Address Blocks
152 //===========================================================================================================================
153
154 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
155
156 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
157
158 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
159
160 static const uint8_t kDNSServerBaseAddrV6[] =
161 {
162 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
163 };
164
165 static const uint8_t kMDNSReplierBaseAddrV6[] =
166 {
167 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
168 };
169
170 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
171 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
172
173 // Bad IPv4 and IPv6 Address Blocks
174 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
175 // in 203.0.113.0/24 nor 2001:db8:1::/120.
176
177 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
178
179 static const uint8_t kDNSServerBadBaseAddrV6[] =
180 {
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
182 };
183
184 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
185
186 //===========================================================================================================================
187 // Misc.
188 //===========================================================================================================================
189
190 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
191 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
192
193 #if( !defined( kWhiteSpaceCharSet ) )
194 #define kWhiteSpaceCharSet "\t\n\v\f\r "
195 #endif
196
197 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
198
199 #if( !defined( strcpy_literal ) )
200 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
201 #endif
202
203 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
204 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
205
206 #define kNoSuchRecordStr "No Such Record"
207 #define kNoSuchRecordAStr "No Such Record (A)"
208 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
209
210 #define kRootLabel ( (const uint8_t *) "" )
211
212 //===========================================================================================================================
213 // Gerneral Command Options
214 //===========================================================================================================================
215
216 // Command option macros
217
218 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
219 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
220 (SHORT_HELP), NULL )
221
222 #define kRequiredOptionSuffix " [REQUIRED]"
223
224 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
225 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
226 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
227 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
228
229 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
230 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
231
232 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
233 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
234 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
235 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
236
237 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
238 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
239 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
240 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
241
242 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
243 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
244
245 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
246 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
247 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
248 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
249
250 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
251 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
252
253 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
254 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
255 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
256 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
257
258 // DNS-SD API flag options
259
260 static int gDNSSDFlags = 0;
261 static int gDNSSDFlag_AllowExpiredAnswers = false;
262 static int gDNSSDFlag_BrowseDomains = false;
263 static int gDNSSDFlag_DenyCellular = false;
264 static int gDNSSDFlag_DenyConstrained = false;
265 static int gDNSSDFlag_DenyExpensive = false;
266 static int gDNSSDFlag_ForceMulticast = false;
267 static int gDNSSDFlag_IncludeAWDL = false;
268 static int gDNSSDFlag_KnownUnique = false;
269 static int gDNSSDFlag_NoAutoRename = false;
270 static int gDNSSDFlag_PathEvaluationDone = false;
271 static int gDNSSDFlag_RegistrationDomains = false;
272 static int gDNSSDFlag_ReturnIntermediates = false;
273 static int gDNSSDFlag_Shared = false;
274 static int gDNSSDFlag_SuppressUnusable = false;
275 static int gDNSSDFlag_Timeout = false;
276 static int gDNSSDFlag_UnicastResponse = false;
277 static int gDNSSDFlag_Unique = false;
278 static int gDNSSDFlag_WakeOnResolve = false;
279
280 #define DNSSDFlagsOption() \
281 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
282 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
283
284 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
285 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
286
287 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
288 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
289 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
290 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
291 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
292 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
293 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
294 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
295 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
296 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
297 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
298 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
299 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
300 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
301 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
302 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
303
304 // Interface option
305
306 static const char * gInterface = NULL;
307
308 #define InterfaceOption() \
309 StringOption( 'i', "interface", &gInterface, "interface", \
310 "Network interface by name or index. Use index -1 for local-only.", false )
311
312 // Connection options
313
314 #define kConnectionArg_Normal ""
315 #define kConnectionArgPrefix_PID "pid:"
316 #define kConnectionArgPrefix_UUID "uuid:"
317
318 static const char * gConnectionOpt = kConnectionArg_Normal;
319
320 #define ConnectionOptions() \
321 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
322 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
323 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
324
325 #define kConnectionSection_Name "Connection Option"
326 #define kConnectionSection_Text \
327 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
328 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
329 "specifying the connection option without an argument, i.e.,\n" \
330 "\n" \
331 " --connection\n" \
332 "\n" \
333 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
334 "\n" \
335 " --connection=pid:<PID>\n" \
336 "\n" \
337 "to specify the delegator by PID, or use\n" \
338 "\n" \
339 " --connection=uuid:<UUID>\n" \
340 "\n" \
341 "to specify the delegator by UUID.\n" \
342 "\n" \
343 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
344 "\n" \
345 " --no-connection\n"
346
347 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
348
349 // Help text for record data options
350
351 #define kRDataArgPrefix_Domain "domain:"
352 #define kRDataArgPrefix_File "file:"
353 #define kRDataArgPrefix_HexString "hex:"
354 #define kRDataArgPrefix_IPv4 "ipv4:"
355 #define kRDataArgPrefix_IPv6 "ipv6:"
356 #define kRDataArgPrefix_SRV "srv:"
357 #define kRDataArgPrefix_String "string:"
358 #define kRDataArgPrefix_TXT "txt:"
359
360 #define kRecordDataSection_Name "Record Data Arguments"
361 #define kRecordDataSection_Text \
362 "A record data argument is specified in one of the following formats:\n" \
363 "\n" \
364 "Format Syntax Example\n" \
365 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
366 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
367 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
368 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
369 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
370 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
371 "String string:<string> string:'\\x09color=red'\n" \
372 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
373 "\n" \
374 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
375
376 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
377
378 //===========================================================================================================================
379 // Output Formatting
380 //===========================================================================================================================
381
382 #define kOutputFormatStr_JSON "json"
383 #define kOutputFormatStr_XML "xml"
384 #define kOutputFormatStr_Binary "binary"
385
386 typedef enum
387 {
388 kOutputFormatType_Invalid = 0,
389 kOutputFormatType_JSON = 1,
390 kOutputFormatType_XML = 2,
391 kOutputFormatType_Binary = 3
392
393 } OutputFormatType;
394
395 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
396 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
397 "\n" \
398 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
399 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
400 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
401 "\n" \
402 )
403
404 //===========================================================================================================================
405 // Browse Command Options
406 //===========================================================================================================================
407
408 static char ** gBrowse_ServiceTypes = NULL;
409 static size_t gBrowse_ServiceTypesCount = 0;
410 static const char * gBrowse_Domain = NULL;
411 static int gBrowse_DoResolve = false;
412 static int gBrowse_QueryTXT = false;
413 static int gBrowse_TimeLimitSecs = 0;
414
415 static CLIOption kBrowseOpts[] =
416 {
417 InterfaceOption(),
418 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
419 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
420
421 CLI_OPTION_GROUP( "Flags" ),
422 DNSSDFlagsOption(),
423 DNSSDFlagsOption_IncludeAWDL(),
424
425 CLI_OPTION_GROUP( "Operation" ),
426 ConnectionOptions(),
427 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
428 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
429 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
430
431 ConnectionSection(),
432 CLI_OPTION_END()
433 };
434
435 //===========================================================================================================================
436 // GetAddrInfo Command Options
437 //===========================================================================================================================
438
439 static const char * gGetAddrInfo_Name = NULL;
440 static int gGetAddrInfo_ProtocolIPv4 = false;
441 static int gGetAddrInfo_ProtocolIPv6 = false;
442 static int gGetAddrInfo_OneShot = false;
443 static int gGetAddrInfo_TimeLimitSecs = 0;
444
445 static CLIOption kGetAddrInfoOpts[] =
446 {
447 InterfaceOption(),
448 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
449 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
450 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
451
452 CLI_OPTION_GROUP( "Flags" ),
453 DNSSDFlagsOption(),
454 DNSSDFlagsOption_AllowExpiredAnswers(),
455 DNSSDFlagsOption_DenyCellular(),
456 DNSSDFlagsOption_DenyConstrained(),
457 DNSSDFlagsOption_DenyExpensive(),
458 DNSSDFlagsOption_PathEvalDone(),
459 DNSSDFlagsOption_ReturnIntermediates(),
460 DNSSDFlagsOption_SuppressUnusable(),
461 DNSSDFlagsOption_Timeout(),
462
463 CLI_OPTION_GROUP( "Operation" ),
464 ConnectionOptions(),
465 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
466 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
467
468 ConnectionSection(),
469 CLI_OPTION_END()
470 };
471
472 //===========================================================================================================================
473 // QueryRecord Command Options
474 //===========================================================================================================================
475
476 static const char * gQueryRecord_Name = NULL;
477 static const char * gQueryRecord_Type = NULL;
478 static int gQueryRecord_OneShot = false;
479 static int gQueryRecord_TimeLimitSecs = 0;
480 static int gQueryRecord_RawRData = false;
481
482 static CLIOption kQueryRecordOpts[] =
483 {
484 InterfaceOption(),
485 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
486 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
487
488 CLI_OPTION_GROUP( "Flags" ),
489 DNSSDFlagsOption(),
490 DNSSDFlagsOption_AllowExpiredAnswers(),
491 DNSSDFlagsOption_DenyCellular(),
492 DNSSDFlagsOption_DenyConstrained(),
493 DNSSDFlagsOption_DenyExpensive(),
494 DNSSDFlagsOption_ForceMulticast(),
495 DNSSDFlagsOption_IncludeAWDL(),
496 DNSSDFlagsOption_PathEvalDone(),
497 DNSSDFlagsOption_ReturnIntermediates(),
498 DNSSDFlagsOption_SuppressUnusable(),
499 DNSSDFlagsOption_Timeout(),
500 DNSSDFlagsOption_UnicastResponse(),
501
502 CLI_OPTION_GROUP( "Operation" ),
503 ConnectionOptions(),
504 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
505 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
506 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
507
508 ConnectionSection(),
509 CLI_OPTION_END()
510 };
511
512 //===========================================================================================================================
513 // Register Command Options
514 //===========================================================================================================================
515
516 static const char * gRegister_Name = NULL;
517 static const char * gRegister_Type = NULL;
518 static const char * gRegister_Domain = NULL;
519 static int gRegister_Port = 0;
520 static const char * gRegister_TXT = NULL;
521 static int gRegister_LifetimeMs = -1;
522 static const char ** gAddRecord_Types = NULL;
523 static size_t gAddRecord_TypesCount = 0;
524 static const char ** gAddRecord_Data = NULL;
525 static size_t gAddRecord_DataCount = 0;
526 static const char ** gAddRecord_TTLs = NULL;
527 static size_t gAddRecord_TTLsCount = 0;
528 static const char * gUpdateRecord_Data = NULL;
529 static int gUpdateRecord_DelayMs = 0;
530 static int gUpdateRecord_TTL = 0;
531
532 static CLIOption kRegisterOpts[] =
533 {
534 InterfaceOption(),
535 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
536 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
537 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
538 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
539 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
540
541 CLI_OPTION_GROUP( "Flags" ),
542 DNSSDFlagsOption(),
543 DNSSDFlagsOption_IncludeAWDL(),
544 DNSSDFlagsOption_KnownUnique(),
545 DNSSDFlagsOption_NoAutoRename(),
546
547 CLI_OPTION_GROUP( "Operation" ),
548 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
549
550 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
551 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
552 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
553 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
554
555 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
556 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
557 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
558 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
559
560 RecordDataSection(),
561 CLI_OPTION_END()
562 };
563
564 //===========================================================================================================================
565 // RegisterRecord Command Options
566 //===========================================================================================================================
567
568 static const char * gRegisterRecord_Name = NULL;
569 static const char * gRegisterRecord_Type = NULL;
570 static const char * gRegisterRecord_Data = NULL;
571 static int gRegisterRecord_TTL = 0;
572 static int gRegisterRecord_LifetimeMs = -1;
573 static const char * gRegisterRecord_UpdateData = NULL;
574 static int gRegisterRecord_UpdateDelayMs = 0;
575 static int gRegisterRecord_UpdateTTL = 0;
576
577 static CLIOption kRegisterRecordOpts[] =
578 {
579 InterfaceOption(),
580 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
581 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
582 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
583 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
584
585 CLI_OPTION_GROUP( "Flags" ),
586 DNSSDFlagsOption(),
587 DNSSDFlagsOption_IncludeAWDL(),
588 DNSSDFlagsOption_KnownUnique(),
589 DNSSDFlagsOption_Shared(),
590 DNSSDFlagsOption_Unique(),
591
592 CLI_OPTION_GROUP( "Operation" ),
593 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
594
595 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
596 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
597 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
598 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
599
600 RecordDataSection(),
601 CLI_OPTION_END()
602 };
603
604 //===========================================================================================================================
605 // Resolve Command Options
606 //===========================================================================================================================
607
608 static char * gResolve_Name = NULL;
609 static char * gResolve_Type = NULL;
610 static char * gResolve_Domain = NULL;
611 static int gResolve_TimeLimitSecs = 0;
612
613 static CLIOption kResolveOpts[] =
614 {
615 InterfaceOption(),
616 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
617 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
618 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
619
620 CLI_OPTION_GROUP( "Flags" ),
621 DNSSDFlagsOption(),
622 DNSSDFlagsOption_ForceMulticast(),
623 DNSSDFlagsOption_IncludeAWDL(),
624 DNSSDFlagsOption_ReturnIntermediates(),
625 DNSSDFlagsOption_WakeOnResolve(),
626
627 CLI_OPTION_GROUP( "Operation" ),
628 ConnectionOptions(),
629 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
630
631 ConnectionSection(),
632 CLI_OPTION_END()
633 };
634
635 //===========================================================================================================================
636 // Reconfirm Command Options
637 //===========================================================================================================================
638
639 static const char * gReconfirmRecord_Name = NULL;
640 static const char * gReconfirmRecord_Type = NULL;
641 static const char * gReconfirmRecord_Class = NULL;
642 static const char * gReconfirmRecord_Data = NULL;
643
644 static CLIOption kReconfirmOpts[] =
645 {
646 InterfaceOption(),
647 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
648 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
649 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
650 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
651
652 CLI_OPTION_GROUP( "Flags" ),
653 DNSSDFlagsOption(),
654
655 RecordDataSection(),
656 CLI_OPTION_END()
657 };
658
659 //===========================================================================================================================
660 // getaddrinfo-POSIX Command Options
661 //===========================================================================================================================
662
663 static const char * gGAIPOSIX_HostName = NULL;
664 static const char * gGAIPOSIX_ServName = NULL;
665 static const char * gGAIPOSIX_Family = NULL;
666 static int gGAIPOSIXFlag_AddrConfig = false;
667 static int gGAIPOSIXFlag_All = false;
668 static int gGAIPOSIXFlag_CanonName = false;
669 static int gGAIPOSIXFlag_NumericHost = false;
670 static int gGAIPOSIXFlag_NumericServ = false;
671 static int gGAIPOSIXFlag_Passive = false;
672 static int gGAIPOSIXFlag_V4Mapped = false;
673 #if( defined( AI_V4MAPPED_CFG ) )
674 static int gGAIPOSIXFlag_V4MappedCFG = false;
675 #endif
676 #if( defined( AI_DEFAULT ) )
677 static int gGAIPOSIXFlag_Default = false;
678 #endif
679 #if( defined( AI_UNUSABLE ) )
680 static int gGAIPOSIXFlag_Unusable = false;
681 #endif
682
683 static CLIOption kGetAddrInfoPOSIXOpts[] =
684 {
685 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
686 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
687
688 CLI_OPTION_GROUP( "Hints" ),
689 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
690 "\n"
691 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
692 "address family is specified, then AF_UNSPEC is used.\n"
693 "\n" ),
694 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
695 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
696 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
697 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
698 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
699 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
700 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
701 #if( defined( AI_V4MAPPED_CFG ) )
702 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
703 #endif
704 #if( defined( AI_DEFAULT ) )
705 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
706 #endif
707 #if( defined( AI_UNUSABLE ) )
708 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
709 #endif
710
711 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
712 CLI_OPTION_END()
713 };
714
715 //===========================================================================================================================
716 // ReverseLookup Command Options
717 //===========================================================================================================================
718
719 static const char * gReverseLookup_IPAddr = NULL;
720 static int gReverseLookup_OneShot = false;
721 static int gReverseLookup_TimeLimitSecs = 0;
722
723 static CLIOption kReverseLookupOpts[] =
724 {
725 InterfaceOption(),
726 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
727
728 CLI_OPTION_GROUP( "Flags" ),
729 DNSSDFlagsOption(),
730 DNSSDFlagsOption_ForceMulticast(),
731 DNSSDFlagsOption_ReturnIntermediates(),
732 DNSSDFlagsOption_SuppressUnusable(),
733
734 CLI_OPTION_GROUP( "Operation" ),
735 ConnectionOptions(),
736 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
737 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
738
739 ConnectionSection(),
740 CLI_OPTION_END()
741 };
742
743 //===========================================================================================================================
744 // PortMapping Command Options
745 //===========================================================================================================================
746
747 static int gPortMapping_ProtocolTCP = false;
748 static int gPortMapping_ProtocolUDP = false;
749 static int gPortMapping_InternalPort = 0;
750 static int gPortMapping_ExternalPort = 0;
751 static int gPortMapping_TTL = 0;
752
753 static CLIOption kPortMappingOpts[] =
754 {
755 InterfaceOption(),
756 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
757 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
758 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
759 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
760 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
761
762 CLI_OPTION_GROUP( "Flags" ),
763 DNSSDFlagsOption(),
764
765 CLI_OPTION_GROUP( "Operation" ),
766 ConnectionOptions(),
767
768 ConnectionSection(),
769 CLI_OPTION_END()
770 };
771
772 //===========================================================================================================================
773 // BrowseAll Command Options
774 //===========================================================================================================================
775
776 static const char * gBrowseAll_Domain = NULL;
777 static const char ** gBrowseAll_ServiceTypes = NULL;
778 static size_t gBrowseAll_ServiceTypesCount = 0;
779 static int gBrowseAll_BrowseTimeSecs = 5;
780 static int gBrowseAll_ConnectTimeout = 0;
781
782 static CLIOption kBrowseAllOpts[] =
783 {
784 InterfaceOption(),
785 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
786 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 ),
787
788 CLI_OPTION_GROUP( "Flags" ),
789 DNSSDFlagsOption_IncludeAWDL(),
790
791 CLI_OPTION_GROUP( "Operation" ),
792 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
793 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
794 CLI_OPTION_END()
795 };
796
797 //===========================================================================================================================
798 // GetNameInfo Command Options
799 //===========================================================================================================================
800
801 static void GetNameInfoCmd( void );
802
803 static char * gGetNameInfo_IPAddress = NULL;
804 static int gGetNameInfoFlag_DGram = false;
805 static int gGetNameInfoFlag_NameReqd = false;
806 static int gGetNameInfoFlag_NoFQDN = false;
807 static int gGetNameInfoFlag_NumericHost = false;
808 static int gGetNameInfoFlag_NumericScope = false;
809 static int gGetNameInfoFlag_NumericServ = false;
810
811 static CLIOption kGetNameInfoOpts[] =
812 {
813 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
814
815 CLI_OPTION_GROUP( "Flags" ),
816 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
817 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
818 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
819 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
820 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
821 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
822
823 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
824 CLI_OPTION_END()
825 };
826
827 //===========================================================================================================================
828 // GetAddrInfoStress Command Options
829 //===========================================================================================================================
830
831 static int gGAIStress_TestDurationSecs = 0;
832 static int gGAIStress_ConnectionCount = 0;
833 static int gGAIStress_DurationMinMs = 0;
834 static int gGAIStress_DurationMaxMs = 0;
835 static int gGAIStress_RequestCountMax = 0;
836
837 static CLIOption kGetAddrInfoStressOpts[] =
838 {
839 InterfaceOption(),
840
841 CLI_OPTION_GROUP( "Flags" ),
842 DNSSDFlagsOption_ReturnIntermediates(),
843 DNSSDFlagsOption_SuppressUnusable(),
844
845 CLI_OPTION_GROUP( "Operation" ),
846 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
847 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
848 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
849 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
850 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
851 CLI_OPTION_END()
852 };
853
854 //===========================================================================================================================
855 // DNSQuery Command Options
856 //===========================================================================================================================
857
858 static char * gDNSQuery_Name = NULL;
859 static char * gDNSQuery_Type = "A";
860 static char * gDNSQuery_Server = NULL;
861 static int gDNSQuery_TimeLimitSecs = 5;
862 static int gDNSQuery_UseTCP = false;
863 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
864 static int gDNSQuery_RawRData = false;
865 static int gDNSQuery_Verbose = false;
866
867 #if( TARGET_OS_DARWIN )
868 #define kDNSQueryServerOptionIsRequired false
869 #else
870 #define kDNSQueryServerOptionIsRequired true
871 #endif
872
873 static CLIOption kDNSQueryOpts[] =
874 {
875 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
876 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
877 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
878 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
879 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
880 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
881 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
882 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
883 CLI_OPTION_END()
884 };
885
886 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
887 //===========================================================================================================================
888 // DNSCrypt Command Options
889 //===========================================================================================================================
890
891 static char * gDNSCrypt_ProviderName = NULL;
892 static char * gDNSCrypt_ProviderKey = NULL;
893 static char * gDNSCrypt_Name = NULL;
894 static char * gDNSCrypt_Type = NULL;
895 static char * gDNSCrypt_Server = NULL;
896 static int gDNSCrypt_TimeLimitSecs = 5;
897 static int gDNSCrypt_RawRData = false;
898 static int gDNSCrypt_Verbose = false;
899
900 static CLIOption kDNSCryptOpts[] =
901 {
902 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
903 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
904 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
905 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
906 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
907 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
908 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
909 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
910 CLI_OPTION_END()
911 };
912 #endif
913
914 //===========================================================================================================================
915 // MDNSQuery Command Options
916 //===========================================================================================================================
917
918 static char * gMDNSQuery_Name = NULL;
919 static char * gMDNSQuery_Type = NULL;
920 static int gMDNSQuery_SourcePort = 0;
921 static int gMDNSQuery_IsQU = false;
922 static int gMDNSQuery_RawRData = false;
923 static int gMDNSQuery_UseIPv4 = false;
924 static int gMDNSQuery_UseIPv6 = false;
925 static int gMDNSQuery_AllResponses = false;
926 static int gMDNSQuery_ReceiveSecs = 1;
927
928 static CLIOption kMDNSQueryOpts[] =
929 {
930 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
931 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
932 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
933 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
934 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
935 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
936 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
937 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
938 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
939 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 ),
940 CLI_OPTION_END()
941 };
942
943 //===========================================================================================================================
944 // MDNSCollider Command Options
945 //===========================================================================================================================
946
947 #define kMDNSColliderProgramSection_Intro \
948 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
949 "ought to react to probe queries that match its record's name, if at all.\n" \
950 "\n" \
951 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
952 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
953 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
954 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
955 "accomplished with the following program:\n" \
956 "\n" \
957 " probes 3r; send; wait 5000\n" \
958 "\n" \
959 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
960 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
961 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
962 "is more than enough time for the collider to respond to probe queries.\n" \
963 "\n" \
964 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
965 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
966 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
967
968 #define kMDNSColliderProgramSection_Probes \
969 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
970 "\n" \
971 "Usage: probes [<action-string>]\n" \
972 "\n" \
973 "The syntax for an action-string is\n" \
974 "\n" \
975 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
976 " <action> ::= [<repeat-count>] <action-code>\n" \
977 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
978 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
979 "\n" \
980 "An expanded action-string is defined as\n" \
981 "\n" \
982 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
983 "\n" \
984 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
985 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
986 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
987 "are not allowed.\n" \
988 "\n" \
989 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
990 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
991 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
992 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
993 "\n" \
994 " Code Action\n" \
995 " ---- ------\n" \
996 " n Do nothing.\n" \
997 " r Respond to the probe query.\n" \
998 " u Respond to the probe query via unicast.\n" \
999 " m Respond to the probe query via multicast.\n" \
1000 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1001 "\n" \
1002 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1003 "a response nor does it multicast its own probe query.\n"
1004
1005 #define kMDNSColliderProgramSection_Send \
1006 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1007 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1008 "\n" \
1009 "Usage: send\n"
1010
1011 #define kMDNSColliderProgramSection_Wait \
1012 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1013 "\n" \
1014 "Usage: wait <milliseconds>\n"
1015
1016 #define kMDNSColliderProgramSection_Loop \
1017 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1018 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1019 "\n" \
1020 "Usage: loop <non-zero count>; ... ; done\n" \
1021 "\n" \
1022 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1023 "\n" \
1024 " loop 3; wait 1000; send; done"
1025
1026 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1027
1028 static const char * gMDNSCollider_Name = NULL;
1029 static const char * gMDNSCollider_Type = NULL;
1030 static const char * gMDNSCollider_RecordData = NULL;
1031 static int gMDNSCollider_UseIPv4 = false;
1032 static int gMDNSCollider_UseIPv6 = false;
1033 static const char * gMDNSCollider_Program = NULL;
1034
1035 static CLIOption kMDNSColliderOpts[] =
1036 {
1037 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1038 StringOption( 'n', "name", &gMDNSCollider_Name, "name", "Collider's record name.", true ),
1039 StringOption( 't', "type", &gMDNSCollider_Type, "type", "Collider's record type.", true ),
1040 StringOption( 'd', "data", &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1041 StringOption( 'p', "program", &gMDNSCollider_Program, "program", "Program to execute. See Program section below.", true ),
1042 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4, "Use IPv4." ),
1043 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6, "Use IPv6." ),
1044
1045 RecordDataSection(),
1046 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro ),
1047 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes ),
1048 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send ),
1049 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait ),
1050 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop ),
1051 CLI_OPTION_END()
1052 };
1053
1054 static void MDNSColliderCmd( void );
1055
1056 //===========================================================================================================================
1057 // PIDToUUID Command Options
1058 //===========================================================================================================================
1059
1060 static int gPIDToUUID_PID = 0;
1061
1062 static CLIOption kPIDToUUIDOpts[] =
1063 {
1064 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1065 CLI_OPTION_END()
1066 };
1067
1068 //===========================================================================================================================
1069 // DNSServer Command Options
1070 //===========================================================================================================================
1071
1072 #define kDNSServerInfoText_Intro \
1073 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1074 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1075 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1076 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1077 "\n" \
1078 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1079
1080 #define kDNSServerInfoText_NameExistence \
1081 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1082 "\n" \
1083 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1084 "order, unless otherwise noted, consist of\n" \
1085 "\n" \
1086 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1087 " 2. at most one Count label;\n" \
1088 " 3. zero or more Tag labels;\n" \
1089 " 4. at most one TTL label; and\n" \
1090 " 5. at most one IPv4 or IPv6 label.\n" \
1091 "\n" \
1092 "An SRV name is defined as a name with the following form:\n" \
1093 "\n" \
1094 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1095 "\n" \
1096 "See \"SRV Names\" for details.\n"
1097
1098 #define kDNSServerInfoText_ResourceRecords \
1099 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1100 "\n" \
1101 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1102 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1103 "\n" \
1104 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1105 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1106 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1107 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1108 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1109 "\n" \
1110 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1111 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1112 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1113 "\n" \
1114 "SRV names are names of SRV records.\n" \
1115 "\n" \
1116 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1117 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1118 "AAAA records with specific TTL values.\n"
1119
1120 #define kDNSServerInfoText_AliasLabel \
1121 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1122 "\n" \
1123 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1124 "exactly N CNAME records:\n" \
1125 "\n" \
1126 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1127 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1128 " name has \"alias-(i - 1)\" as its first label.\n" \
1129 "\n" \
1130 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1131 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1132 " \"alias\" instead.\n" \
1133 "\n" \
1134 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1135 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1136 "\n" \
1137 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1138 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1139 "QNAME minus its first label.\n" \
1140 "\n" \
1141 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1142 "records:\n" \
1143 "\n" \
1144 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1145 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1146 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1147 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1148
1149 #define kDNSServerInfoText_AliasTTLLabel \
1150 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1151 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1152 "\n" \
1153 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1154 "will contain exactly N CNAME records:\n" \
1155 "\n" \
1156 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1157 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1158 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1159 "\n" \
1160 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1161 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1162 " label.\n" \
1163 "\n" \
1164 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1165 "CNAME records:\n" \
1166 "\n" \
1167 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1168 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1169 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1170
1171 #define kDNSServerInfoText_CountLabel \
1172 "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" \
1173 "an integer in [N_1, 255].\n" \
1174 "\n" \
1175 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1176 "QTYPE, then the response will contain exactly N address records:\n" \
1177 "\n" \
1178 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1179 " and whose RDATA is an address equal to a constant base address + i.\n" \
1180 "\n" \
1181 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1182 "\n" \
1183 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1184 "records:\n" \
1185 "\n" \
1186 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1187 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1188 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1189 "\n" \
1190 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1191 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1192 "\n" \
1193 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1194 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1195 "\n" \
1196 " 2. The order of the address records will be random.\n" \
1197 "\n" \
1198 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1199 "following AAAA records:\n" \
1200 "\n" \
1201 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1202 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1203 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1204 "\n" \
1205 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1206 "will contain no address records, regardless of whether it contains a Count label.\n" \
1207 "\n" \
1208 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1209 "count-1\".\n"
1210
1211 #define kDNSServerInfoText_TagLabel \
1212 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1213 "\n" \
1214 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1215 "to increase the sizes of domain names.\n"
1216
1217 #define kDNSServerInfoText_TTLLabel \
1218 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1219 "\n" \
1220 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1221 "response will have a TTL value equal to T.\n"
1222
1223 #define kDNSServerInfoText_IPv4Label \
1224 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1225
1226 #define kDNSServerInfoText_IPv6Label \
1227 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1228
1229 #define kDNSServerInfoText_SRVNames \
1230 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1231 "\n" \
1232 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1233 "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" \
1234 "the target hostname of each of the SRV name's SRV records.\n" \
1235 "\n" \
1236 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1237 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1238 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1239 "d.test. labels, whichever comes first.\n" \
1240 "\n" \
1241 "Example. A response to an SRV record query with a QNAME of\n" \
1242 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1243 "\n" \
1244 "_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" \
1245 "_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"
1246
1247 #define kDNSServerInfoText_BadUDPMode \
1248 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1249 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1250 "that's not equal to the query's message ID.\n" \
1251 "\n" \
1252 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1253 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1254 "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" \
1255 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1256 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1257
1258 static int gDNSServer_LoopbackOnly = false;
1259 static int gDNSServer_Foreground = false;
1260 static int gDNSServer_ResponseDelayMs = 0;
1261 static int gDNSServer_DefaultTTL = 60;
1262 static int gDNSServer_Port = kDNSPort;
1263 static const char * gDNSServer_DomainOverride = NULL;
1264 #if( TARGET_OS_DARWIN )
1265 static const char * gDNSServer_FollowPID = NULL;
1266 #endif
1267 static int gDNSServer_BadUDPMode = false;
1268
1269 static CLIOption kDNSServerOpts[] =
1270 {
1271 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ),
1272 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Direct log output to stdout instead of system logging." ),
1273 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1274 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1275 IntegerOption( 'p', "port", &gDNSServer_Port, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1276 StringOption( 0 , "domain", &gDNSServer_DomainOverride, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1277 #if( TARGET_OS_DARWIN )
1278 StringOption( 0 , "follow", &gDNSServer_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1279 #endif
1280 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1281
1282 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1283 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1284 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1285 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1286 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1287 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1288 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1289 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1290 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1291 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1292 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames ),
1293 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode ),
1294 CLI_OPTION_END()
1295 };
1296
1297 static void DNSServerCmd( void );
1298
1299 //===========================================================================================================================
1300 // MDNSReplier Command Options
1301 //===========================================================================================================================
1302
1303 #define kMDNSReplierPortBase 50000
1304
1305 #define kMDNSReplierInfoText_Intro \
1306 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1307 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1308 "\n" \
1309 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1310
1311 #define kMDNSReplierInfoText_Parameters \
1312 "There are five parameters that control the replier's set of authoritative records.\n" \
1313 "\n" \
1314 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1315 " parameter is specified with the --hostname option.\n" \
1316 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1317 " option.\n" \
1318 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1319 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1320 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1321 " --maxInstanceCount option.\n" \
1322 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1323 " with the --countA option.\n" \
1324 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1325 " specified with the --countAAAA option.\n"
1326
1327 #define kMDNSReplierInfoText_PTR \
1328 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1329 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1330 "\n" \
1331 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1332 "\n" \
1333 " 1. The first PTR record is defined as\n" \
1334 "\n" \
1335 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1336 " TYPE: PTR\n" \
1337 " CLASS: IN\n" \
1338 " TTL: 4500\n" \
1339 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1340 "\n" \
1341 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1342 "\n" \
1343 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1344 " TYPE: PTR\n" \
1345 " CLASS: IN\n" \
1346 " TTL: 4500\n" \
1347 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1348
1349 #define kMDNSReplierInfoText_SRV \
1350 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1351 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1352 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1353 "\n" \
1354 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1355 "\n" \
1356 " 1. The first SRV record is defined as\n" \
1357 "\n" \
1358 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1359 " TYPE: SRV\n" \
1360 " CLASS: IN\n" \
1361 " TTL: 120\n" \
1362 " RDATA:\n" \
1363 " Priority: 0\n" \
1364 " Weight: 0\n" \
1365 " Port: (50000 + L) mod 2^16\n" \
1366 " Target: <hostname>.local.\n" \
1367 "\n" \
1368 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1369 "\n" \
1370 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1371 " TYPE: SRV\n" \
1372 " CLASS: IN\n" \
1373 " TTL: 120\n" \
1374 " RDATA:\n" \
1375 " Priority: 0\n" \
1376 " Weight: 0\n" \
1377 " Port: (50000 + L) mod 2^16\n" \
1378 " Target: <hostname>-<i>.local.\n"
1379
1380 #define kMDNSReplierInfoText_TXT \
1381 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1382 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1383 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1384 "\n" \
1385 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1386 "\n" \
1387 " 1. The first TXT record is defined as\n" \
1388 "\n" \
1389 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1390 " TYPE: TXT\n" \
1391 " CLASS: IN\n" \
1392 " TTL: 4500\n" \
1393 " RDLENGTH: L\n" \
1394 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1395 "\n" \
1396 " 2. For each i in [2, N], there is one TXT record:\n" \
1397 "\n" \
1398 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1399 " TYPE: TXT\n" \
1400 " CLASS: IN\n" \
1401 " TTL: 4500\n" \
1402 " RDLENGTH: L\n" \
1403 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1404 "\n" \
1405 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1406 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1407 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1408
1409 #define kMDNSReplierInfoText_A \
1410 "The replier has exactly N_max x N_a authoritative A records:\n" \
1411 "\n" \
1412 " 1. For each j in [1, N_a], an A record is defined as\n" \
1413 "\n" \
1414 " NAME: <hostname>.local.\n" \
1415 " TYPE: A\n" \
1416 " CLASS: IN\n" \
1417 " TTL: 120\n" \
1418 " RDLENGTH: 4\n" \
1419 " RDATA: 0.0.1.<j>\n" \
1420 "\n" \
1421 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1422 "\n" \
1423 " NAME: <hostname>-<i>.local.\n" \
1424 " TYPE: A\n" \
1425 " CLASS: IN\n" \
1426 " TTL: 120\n" \
1427 " RDLENGTH: 4\n" \
1428 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1429
1430 #define kMDNSReplierInfoText_AAAA \
1431 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1432 "\n" \
1433 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1434 "\n" \
1435 " NAME: <hostname>.local.\n" \
1436 " TYPE: AAAA\n" \
1437 " CLASS: IN\n" \
1438 " TTL: 120\n" \
1439 " RDLENGTH: 16\n" \
1440 " RDATA: 2001:db8:2::1:<j>\n" \
1441 "\n" \
1442 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1443 "\n" \
1444 " NAME: <hostname>-<i>.local.\n" \
1445 " TYPE: AAAA\n" \
1446 " CLASS: IN\n" \
1447 " TTL: 120\n" \
1448 " RDLENGTH: 16\n" \
1449 " RDATA: 2001:db8:2::<i>:<j>\n"
1450
1451 #define kMDNSReplierInfoText_Responses \
1452 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1453 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1454 "separate response messages.\n"
1455
1456 static const char * gMDNSReplier_Hostname = NULL;
1457 static const char * gMDNSReplier_ServiceTypeTag = NULL;
1458 static int gMDNSReplier_MaxInstanceCount = 1000;
1459 static int gMDNSReplier_NoAdditionals = false;
1460 static int gMDNSReplier_RecordCountA = 1;
1461 static int gMDNSReplier_RecordCountAAAA = 1;
1462 static double gMDNSReplier_UnicastDropRate = 0.0;
1463 static double gMDNSReplier_MulticastDropRate = 0.0;
1464 static int gMDNSReplier_MaxDropCount = 0;
1465 static int gMDNSReplier_UseIPv4 = false;
1466 static int gMDNSReplier_UseIPv6 = false;
1467 static int gMDNSReplier_Foreground = false;
1468 static const char * gMDNSReplier_FollowPID = NULL;
1469
1470 static CLIOption kMDNSReplierOpts[] =
1471 {
1472 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1473 StringOption( 'n', "hostname", &gMDNSReplier_Hostname, "string", "Base name to use for hostnames and service instance names.", true ),
1474 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1475 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount, "count", "Maximum number of service instances. (default: 1000)", false ),
1476 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals, "When answering queries, don't include any additional records." ),
1477 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1478 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1479 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1480 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1481 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1482 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4, "Use IPv4." ),
1483 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6, "Use IPv6." ),
1484 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground, "Direct log output to stdout instead of system logging." ),
1485 #if( TARGET_OS_DARWIN )
1486 StringOption( 0 , "follow", &gMDNSReplier_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1487 #endif
1488
1489 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro ),
1490 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters ),
1491 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR ),
1492 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV ),
1493 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT ),
1494 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A ),
1495 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA ),
1496 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses ),
1497 CLI_OPTION_END()
1498 };
1499
1500 static void MDNSReplierCmd( void );
1501
1502 //===========================================================================================================================
1503 // Test Command Options
1504 //===========================================================================================================================
1505
1506 #define kTestExitStatusSection_Name "Exit Status"
1507 #define kTestExitStatusSection_Text \
1508 "This test command can exit with one of three status codes:\n" \
1509 "\n" \
1510 "0 - The test ran to completion and passed.\n" \
1511 "1 - A fatal error prevented the test from completing.\n" \
1512 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1513 "\n" \
1514 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1515 "performance.\n"
1516
1517 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1518
1519 #define kGAIPerfTestSuiteName_Basic "basic"
1520 #define kGAIPerfTestSuiteName_Advanced "advanced"
1521
1522 static const char * gGAIPerf_TestSuite = NULL;
1523 static int gGAIPerf_CallDelayMs = 10;
1524 static int gGAIPerf_ServerDelayMs = 10;
1525 static int gGAIPerf_SkipPathEvalulation = false;
1526 static int gGAIPerf_BadUDPMode = false;
1527 static int gGAIPerf_IterationCount = 100;
1528 static int gGAIPerf_IterationTimeLimitMs = 100;
1529 static const char * gGAIPerf_OutputFilePath = NULL;
1530 static const char * gGAIPerf_OutputFormat = kOutputFormatStr_JSON;
1531 static int gGAIPerf_OutputAppendNewline = false;
1532
1533 static void GAIPerfCmd( void );
1534
1535 #define kGAIPerfSectionText_TestSuiteBasic \
1536 "This test suite consists of the following three test cases:\n" \
1537 "\n" \
1538 "Test Case #1: Resolve a domain name with\n" \
1539 "\n" \
1540 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1541 "\n" \
1542 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1543 "server queries.\n" \
1544 "\n" \
1545 "Test Case #2: Resolve a domain name with\n" \
1546 "\n" \
1547 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1548 "\n" \
1549 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1550 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1551 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1552 "\n" \
1553 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1554 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1555 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1556 "\n" \
1557 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1558
1559 #define kGAIPerfSectionText_TestSuiteAdvanced \
1560 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1561 "\n" \
1562 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1563 "\n" \
1564 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1565 "\n" \
1566 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1567 "server queries.\n" \
1568 "\n" \
1569 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1570 "\n" \
1571 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1572 "\n" \
1573 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1574 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1575 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1576 "\n" \
1577 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1578 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1579 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1580 "\n" \
1581 "N_c and N_a take on the following values, depending on the value of N:\n" \
1582 "\n" \
1583 " N_c is 0 if N is in [1, 8].\n" \
1584 " N_c is 1 if N is in [9, 16].\n" \
1585 " N_c is 2 if N is in [17, 24].\n" \
1586 " N_c is 4 if N is in [25, 32].\n" \
1587 "\n" \
1588 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1589 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1590 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1591 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1592 "\n" \
1593 "Finally,\n" \
1594 "\n" \
1595 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1596
1597 static CLIOption kGAIPerfOpts[] =
1598 {
1599 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1600 "\n"
1601 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1602 "\n"
1603 ),
1604 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1605 FormatOption( 'f', "format", &gGAIPerf_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1606 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1607 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount, "count", "The number of iterations per test case. (default: 100)", false ),
1608 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1609 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1610 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1611
1612 CLI_OPTION_GROUP( "DNS Server Options" ),
1613 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1614 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1615
1616 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic ),
1617 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced ),
1618 TestExitStatusSection(),
1619 CLI_OPTION_END()
1620 };
1621
1622 static void MDNSDiscoveryTestCmd( void );
1623
1624 static int gMDNSDiscoveryTest_InstanceCount = 100;
1625 static int gMDNSDiscoveryTest_TXTSize = 100;
1626 static int gMDNSDiscoveryTest_BrowseTimeSecs = 2;
1627 static int gMDNSDiscoveryTest_FlushCache = false;
1628 static char * gMDNSDiscoveryTest_Interface = NULL;
1629 static int gMDNSDiscoveryTest_NoAdditionals = false;
1630 static int gMDNSDiscoveryTest_RecordCountA = 1;
1631 static int gMDNSDiscoveryTest_RecordCountAAAA = 1;
1632 static double gMDNSDiscoveryTest_UnicastDropRate = 0.0;
1633 static double gMDNSDiscoveryTest_MulticastDropRate = 0.0;
1634 static int gMDNSDiscoveryTest_MaxDropCount = 0;
1635 static int gMDNSDiscoveryTest_UseIPv4 = false;
1636 static int gMDNSDiscoveryTest_UseIPv6 = false;
1637 static const char * gMDNSDiscoveryTest_OutputFormat = kOutputFormatStr_JSON;
1638 static int gMDNSDiscoveryTest_OutputAppendNewline = false;
1639 static const char * gMDNSDiscoveryTest_OutputFilePath = NULL;
1640
1641 static CLIOption kMDNSDiscoveryTestOpts[] =
1642 {
1643 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount, "count", "Number of service instances to discover. (default: 100)", false ),
1644 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1645 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1646 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1647
1648 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1649 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1650 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals, "When answering queries, don't include any additional records." ),
1651 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1652 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1653 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1654 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1655 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1656 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4, "Use IPv4." ),
1657 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6, "Use IPv6." ),
1658
1659 CLI_OPTION_GROUP( "Results" ),
1660 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1661 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1662
1663 TestExitStatusSection(),
1664 CLI_OPTION_END()
1665 };
1666
1667 static void DotLocalTestCmd( void );
1668
1669 static const char * gDotLocalTest_Interface = NULL;
1670 static const char * gDotLocalTest_OutputFormat = kOutputFormatStr_JSON;
1671 static const char * gDotLocalTest_OutputFilePath = NULL;
1672
1673 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1674 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1675 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1676 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1677 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1678 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1679 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1680
1681 #define kDotLocalTestSectionText_Description \
1682 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1683 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1684 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1685 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1686 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1687 "then the mdnsreplier's base hostname is test.local.\n" \
1688 "\n" \
1689 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1690 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1691 "\n" \
1692 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1693 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1694 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1695 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1696 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1697 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1698 "\n" \
1699 "Each subtest runs for five seconds.\n"
1700
1701 static CLIOption kDotLocalTestOpts[] =
1702 {
1703 StringOption( 'i', "interface", &gDotLocalTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1704
1705 CLI_OPTION_GROUP( "Results" ),
1706 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1707 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1708
1709 CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1710 TestExitStatusSection(),
1711 CLI_OPTION_END()
1712 };
1713
1714 static void ProbeConflictTestCmd( void );
1715
1716 static const char * gProbeConflictTest_Interface = NULL;
1717 static int gProbeConflictTest_UseComputerName = false;
1718 static const char * gProbeConflictTest_OutputFormat = kOutputFormatStr_JSON;
1719 static const char * gProbeConflictTest_OutputFilePath = NULL;
1720
1721 static CLIOption kProbeConflictTestOpts[] =
1722 {
1723 StringOption( 'i', "interface", &gProbeConflictTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1724 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1725
1726 CLI_OPTION_GROUP( "Results" ),
1727 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat, "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1728 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1729
1730 TestExitStatusSection(),
1731 CLI_OPTION_END()
1732 };
1733
1734 static void RegistrationTestCmd( void );
1735
1736 static int gRegistrationTest_BATSEnvironment = false;
1737 static const char * gRegistrationTest_OutputFormat = kOutputFormatStr_JSON;
1738 static const char * gRegistrationTest_OutputFilePath = NULL;
1739
1740 static CLIOption kRegistrationTestOpts[] =
1741 {
1742 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment, "Informs the test that it's running in a BATS environment.",
1743 "\n"
1744 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1745 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1746 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1747 "\n"
1748 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1749 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1750 ),
1751 CLI_OPTION_GROUP( "Results" ),
1752 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1753 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1754
1755 TestExitStatusSection(),
1756 CLI_OPTION_END()
1757 };
1758
1759 static void ExpensiveConstrainedTestCmd( void );
1760
1761 static const char * gExpensiveConstrainedTest_Interface = NULL;
1762 static const char * gExpensiveConstrainedTest_Name = NULL;
1763 static Boolean gExpensiveConstrainedTest_DenyExpensive = false;
1764 static Boolean gExpensiveConstrainedTest_DenyConstrained = false;
1765 static Boolean gExpensiveConstrainedTest_StartFromExpensive = false;
1766 static int gExpensiveConstrainedTest_ProtocolIPv4 = false;
1767 static int gExpensiveConstrainedTest_ProtocolIPv6 = false;
1768 static const char * gExpensiveConstrainedTest_OutputFormat = kOutputFormatStr_JSON;
1769 static const char * gExpensiveConstrainedTest_OutputFilePath = NULL;
1770
1771 static CLIOption kExpensiveConstrainedTestOpts[] =
1772 {
1773 CLI_OPTION_GROUP( "Results" ),
1774 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1775 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1776
1777 TestExitStatusSection(),
1778 CLI_OPTION_END()
1779 };
1780
1781 #if( MDNSRESPONDER_PROJECT )
1782 static void XCTestCmd( void );
1783
1784 static const char * gXCTest_Classname = NULL;
1785
1786 static CLIOption kXCTestOpts[] =
1787 {
1788 StringOption( 'c', "class", &gXCTest_Classname, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
1789 CLI_OPTION_END()
1790 };
1791 #endif
1792
1793 static CLIOption kTestOpts[] =
1794 {
1795 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1796 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts, "Tests mDNS service discovery for correctness.", false ),
1797 Command( "dotlocal", DotLocalTestCmd, kDotLocalTestOpts, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1798 Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts, "Tests various probing conflict scenarios.", false ),
1799 Command( "registration", RegistrationTestCmd, kRegistrationTestOpts, "Tests service registrations.", false ),
1800 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd, kExpensiveConstrainedTestOpts, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1801 #if( MDNSRESPONDER_PROJECT )
1802 Command( "xctest", XCTestCmd, kXCTestOpts, "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
1803 #endif
1804 CLI_OPTION_END()
1805 };
1806
1807 //===========================================================================================================================
1808 // SSDP Command Options
1809 //===========================================================================================================================
1810
1811 static int gSSDPDiscover_MX = 1;
1812 static const char * gSSDPDiscover_ST = "ssdp:all";
1813 static int gSSDPDiscover_ReceiveSecs = 1;
1814 static int gSSDPDiscover_UseIPv4 = false;
1815 static int gSSDPDiscover_UseIPv6 = false;
1816 static int gSSDPDiscover_Verbose = false;
1817
1818 static CLIOption kSSDPDiscoverOpts[] =
1819 {
1820 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1821 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1822 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1823 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1824 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1825 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1826 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
1827 CLI_OPTION_END()
1828 };
1829
1830 static void SSDPDiscoverCmd( void );
1831
1832 static CLIOption kSSDPOpts[] =
1833 {
1834 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
1835 CLI_OPTION_END()
1836 };
1837
1838 #if( TARGET_OS_DARWIN )
1839 //===========================================================================================================================
1840 // res_query Command Options
1841 //===========================================================================================================================
1842
1843 static void ResQueryCmd( void );
1844
1845 static const char * gResQuery_Name = NULL;
1846 static const char * gResQuery_Type = NULL;
1847 static const char * gResQuery_Class = NULL;
1848 static int gResQuery_UseLibInfo = false;
1849
1850 static CLIOption kResQueryOpts[] =
1851 {
1852 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
1853 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1854 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1855 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
1856 CLI_OPTION_END()
1857 };
1858
1859 //===========================================================================================================================
1860 // dns_query Command Options
1861 //===========================================================================================================================
1862
1863 static void ResolvDNSQueryCmd( void );
1864
1865 static const char * gResolvDNSQuery_Name = NULL;
1866 static const char * gResolvDNSQuery_Type = NULL;
1867 static const char * gResolvDNSQuery_Class = NULL;
1868 static const char * gResolvDNSQuery_Path = NULL;
1869
1870 static CLIOption kResolvDNSQueryOpts[] =
1871 {
1872 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
1873 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1874 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1875 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1876 CLI_OPTION_END()
1877 };
1878
1879 //===========================================================================================================================
1880 // CFHost Command Options
1881 //===========================================================================================================================
1882
1883 static void CFHostCmd( void );
1884
1885 static const char * gCFHost_Name = NULL;
1886 static int gCFHost_WaitSecs = 0;
1887
1888 static CLIOption kCFHostOpts[] =
1889 {
1890 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
1891 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1892 CLI_OPTION_END()
1893 };
1894
1895 static CLIOption kLegacyOpts[] =
1896 {
1897 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1898 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
1899 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
1900 CLI_OPTION_END()
1901 };
1902
1903 //===========================================================================================================================
1904 // DNSConfigAdd Command Options
1905 //===========================================================================================================================
1906
1907 static void DNSConfigAddCmd( void );
1908
1909 static CFStringRef gDNSConfigAdd_ID = NULL;
1910 static char ** gDNSConfigAdd_IPAddrArray = NULL;
1911 static size_t gDNSConfigAdd_IPAddrCount = 0;
1912 static char ** gDNSConfigAdd_DomainArray = NULL;
1913 static size_t gDNSConfigAdd_DomainCount = 0;
1914 static const char * gDNSConfigAdd_Interface = NULL;
1915
1916 static CLIOption kDNSConfigAddOpts[] =
1917 {
1918 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
1919 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1920 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1921 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
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 //===========================================================================================================================
1928 // DNSConfigRemove Command Options
1929 //===========================================================================================================================
1930
1931 static void DNSConfigRemoveCmd( void );
1932
1933 static CFStringRef gDNSConfigRemove_ID = NULL;
1934
1935 static CLIOption kDNSConfigRemoveOpts[] =
1936 {
1937 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
1938
1939 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1940 CLI_OPTION_END()
1941 };
1942
1943 static CLIOption kDNSConfigOpts[] =
1944 {
1945 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1946 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1947 CLI_OPTION_END()
1948 };
1949
1950 //===========================================================================================================================
1951 // XPCSend
1952 //===========================================================================================================================
1953
1954 static void XPCSendCmd( void );
1955
1956 static const char * gXPCSend_ServiceName = NULL;
1957 static const char * gXPCSend_MessageStr = NULL;
1958
1959 static const char kXPCSendMessageSection_Name[] = "Message Argument";
1960 static const char kXPCSendMessageSection_Text[] =
1961 "XPC messages are described as a string using the following syntax.\n"
1962 "\n"
1963 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
1964 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
1965 "currently supported XPC types.\n"
1966 "\n"
1967 "Arrays begin with a '[' and end with a ']'.\n"
1968 "\n"
1969 "The following non-container XPC types are supported:\n"
1970 "\n"
1971 "Type Syntax Example\n"
1972 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
1973 "data data:<hex string> data:C0000201\n"
1974 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
1975 "string string:<string> string:hello\\ world\n"
1976 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
1977 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
1978 "\n"
1979 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
1980 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
1981
1982 static CLIOption kXPCSendOpts[] =
1983 {
1984 StringOption( 's', "service", &gXPCSend_ServiceName, "service name", "XPC service name.", true ),
1985 StringOption( 'm', "message", &gXPCSend_MessageStr, "message", "XPC message as a string.", true ),
1986
1987 CLI_SECTION( kXPCSendMessageSection_Name, kXPCSendMessageSection_Text ),
1988 CLI_OPTION_END()
1989 };
1990 #endif // TARGET_OS_DARWIN
1991
1992 #if( MDNSRESPONDER_PROJECT )
1993 //===========================================================================================================================
1994 // InterfaceMonitor
1995 //===========================================================================================================================
1996
1997 static void InterfaceMonitorCmd( void );
1998
1999 static CLIOption kInterfaceMonitorOpts[] =
2000 {
2001 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
2002 CLI_OPTION_END()
2003 };
2004
2005 //===========================================================================================================================
2006 // DNSProxy
2007 //===========================================================================================================================
2008
2009 static void DNSProxyCmd( void );
2010
2011 static char ** gDNSProxy_InputInterfaces = NULL;
2012 static size_t gDNSProxy_InputInterfaceCount = 0;
2013 static const char * gDNSProxy_OutputInterface = NULL;
2014
2015 static CLIOption kDNSProxyOpts[] =
2016 {
2017 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces, &gDNSProxy_InputInterfaceCount, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2018 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2019 CLI_OPTION_END()
2020 };
2021
2022 #endif // MDNSRESPONDER_PROJECT
2023
2024 //===========================================================================================================================
2025 // Command Table
2026 //===========================================================================================================================
2027
2028 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2029
2030 static void BrowseCmd( void );
2031 static void GetAddrInfoCmd( void );
2032 static void QueryRecordCmd( void );
2033 static void RegisterCmd( void );
2034 static void RegisterRecordCmd( void );
2035 static void ResolveCmd( void );
2036 static void ReconfirmCmd( void );
2037 static void GetAddrInfoPOSIXCmd( void );
2038 static void ReverseLookupCmd( void );
2039 static void PortMappingCmd( void );
2040 static void BrowseAllCmd( void );
2041 static void GetAddrInfoStressCmd( void );
2042 static void DNSQueryCmd( void );
2043 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2044 static void DNSCryptCmd( void );
2045 #endif
2046 static void MDNSQueryCmd( void );
2047 static void PIDToUUIDCmd( void );
2048 static void DaemonVersionCmd( void );
2049
2050 static CLIOption kGlobalOpts[] =
2051 {
2052 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2053 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2054 CLI_OPTION_HELP(),
2055
2056 // Common commands.
2057
2058 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2059 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2060 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2061 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2062 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2063 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2064 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2065 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2066 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2067 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2068 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2069
2070 // Uncommon commands.
2071
2072 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2073 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2074 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2075 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2076 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2077 #endif
2078 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2079 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2080 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2081 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2082 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2083 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2084 Command( "ssdp", NULL, kSSDPOpts, "Simple Service Discovery Protocol (SSDP).", true ),
2085 #if( TARGET_OS_DARWIN )
2086 Command( "legacy", NULL, kLegacyOpts, "Legacy DNS API.", true ),
2087 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2088 Command( "xpcsend", XPCSendCmd, kXPCSendOpts, "Sends a message to an XPC service.", true ),
2089 #endif
2090 #if( MDNSRESPONDER_PROJECT )
2091 Command( "interfaceMonitor", InterfaceMonitorCmd, kInterfaceMonitorOpts, "mDNSResponder's interface monitor.", true ),
2092 Command( "dnsproxy", DNSProxyCmd, kDNSProxyOpts, "Enables mDNSResponder's DNS proxy.", true ),
2093 #endif
2094 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2095
2096 CLI_COMMAND_HELP(),
2097 CLI_OPTION_END()
2098 };
2099
2100 //===========================================================================================================================
2101 // Helper Prototypes
2102 //===========================================================================================================================
2103
2104 #define kExitReason_OneShotDone "one-shot done"
2105 #define kExitReason_ReceivedResponse "received response"
2106 #define kExitReason_SIGINT "interrupt signal"
2107 #define kExitReason_Timeout "timeout"
2108 #define kExitReason_TimeLimit "time limit"
2109
2110 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2111
2112 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2113
2114 typedef enum
2115 {
2116 kConnectionType_None = 0,
2117 kConnectionType_Normal = 1,
2118 kConnectionType_DelegatePID = 2,
2119 kConnectionType_DelegateUUID = 3
2120
2121 } ConnectionType;
2122
2123 typedef struct
2124 {
2125 ConnectionType type;
2126 union
2127 {
2128 int32_t pid;
2129 uint8_t uuid[ 16 ];
2130
2131 } delegate;
2132
2133 } ConnectionDesc;
2134
2135 static OSStatus
2136 CreateConnectionFromArgString(
2137 const char * inString,
2138 dispatch_queue_t inQueue,
2139 DNSServiceRef * outSDRef,
2140 ConnectionDesc * outDesc );
2141 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2142 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2143 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2144 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2145
2146 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2147
2148 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2149 static const char * RecordTypeToString( unsigned int inValue );
2150
2151 static OSStatus
2152 DNSRecordDataToString(
2153 const void * inRDataPtr,
2154 size_t inRDataLen,
2155 unsigned int inRDataType,
2156 const void * inMsgPtr,
2157 size_t inMsgLen,
2158 char ** outString );
2159
2160 static OSStatus
2161 DNSMessageToText(
2162 const uint8_t * inMsgPtr,
2163 size_t inMsgLen,
2164 Boolean inIsMDNS,
2165 Boolean inPrintRaw,
2166 char ** outText );
2167
2168 static OSStatus
2169 WriteDNSQueryMessage(
2170 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2171 uint16_t inMsgID,
2172 uint16_t inFlags,
2173 const char * inQName,
2174 uint16_t inQType,
2175 uint16_t inQClass,
2176 size_t * outMsgLen );
2177
2178 // Dispatch helpers
2179
2180 typedef void ( *DispatchHandler )( void *inContext );
2181
2182 static OSStatus
2183 DispatchSignalSourceCreate(
2184 int inSignal,
2185 DispatchHandler inEventHandler,
2186 void * inContext,
2187 dispatch_source_t * outSource );
2188 static OSStatus
2189 DispatchSocketSourceCreate(
2190 SocketRef inSock,
2191 dispatch_source_type_t inType,
2192 dispatch_queue_t inQueue,
2193 DispatchHandler inEventHandler,
2194 DispatchHandler inCancelHandler,
2195 void * inContext,
2196 dispatch_source_t * outSource );
2197
2198 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2199 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2200
2201 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2202 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2203
2204 static OSStatus
2205 DispatchTimerCreate(
2206 dispatch_time_t inStart,
2207 uint64_t inIntervalNs,
2208 uint64_t inLeewayNs,
2209 dispatch_queue_t inQueue,
2210 DispatchHandler inEventHandler,
2211 DispatchHandler inCancelHandler,
2212 void * inContext,
2213 dispatch_source_t * outTimer );
2214
2215 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2216 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2217
2218 static OSStatus
2219 DispatchProcessMonitorCreate(
2220 pid_t inPID,
2221 unsigned long inFlags,
2222 dispatch_queue_t inQueue,
2223 DispatchHandler inEventHandler,
2224 DispatchHandler inCancelHandler,
2225 void * inContext,
2226 dispatch_source_t * outMonitor );
2227
2228 static const char * ServiceTypeDescription( const char *inName );
2229
2230 typedef struct
2231 {
2232 SocketRef sock; // Socket.
2233 void * userContext; // User context.
2234 int32_t refCount; // Reference count.
2235
2236 } SocketContext;
2237
2238 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
2239 static SocketContext * SocketContextRetain( SocketContext *inContext );
2240 static void SocketContextRelease( SocketContext *inContext );
2241 static void SocketContextCancelHandler( void *inContext );
2242
2243 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2244
2245 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2246 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2247 #if( TARGET_OS_DARWIN )
2248 static int64_t _StringToInt64( const char *inString, OSStatus *outError );
2249 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError );
2250 static pid_t _StringToPID( const char *inString, OSStatus *outError );
2251 static OSStatus
2252 _ParseEscapedString(
2253 const char * inSrc,
2254 const char * inEnd,
2255 const char * inDelimiters,
2256 char * inBufPtr,
2257 size_t inBufLen,
2258 size_t * outCopiedLen,
2259 size_t * outActualLen,
2260 const char ** outPtr );
2261 #endif
2262 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2263 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2264 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2265 #if( TARGET_OS_DARWIN )
2266 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2267 #endif
2268 static OSStatus
2269 _ServerSocketOpenEx2(
2270 int inFamily,
2271 int inType,
2272 int inProtocol,
2273 const void * inAddr,
2274 int inPort,
2275 int * outPort,
2276 int inRcvBufSize,
2277 Boolean inNoPortReuse,
2278 SocketRef * outSock );
2279
2280 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2281 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2282
2283 static OSStatus
2284 CreateMulticastSocket(
2285 const struct sockaddr * inAddr,
2286 int inPort,
2287 const char * inIfName,
2288 uint32_t inIfIndex,
2289 Boolean inJoin,
2290 int * outPort,
2291 SocketRef * outSock );
2292
2293 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2294 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2295 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2296 static OSStatus CheckRootUser( void );
2297 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... );
2298 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2299 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2300 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2301 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2302 static OSStatus
2303 CreateNSECRecordData(
2304 const uint8_t * inNextDomainName,
2305 uint8_t ** outPtr,
2306 size_t * outLen,
2307 unsigned int inTypeCount,
2308 ... );
2309 static OSStatus
2310 AppendSOARecord(
2311 DataBuffer * inDB,
2312 const uint8_t * inNamePtr,
2313 size_t inNameLen,
2314 uint16_t inType,
2315 uint16_t inClass,
2316 uint32_t inTTL,
2317 const uint8_t * inMName,
2318 const uint8_t * inRName,
2319 uint32_t inSerial,
2320 uint32_t inRefresh,
2321 uint32_t inRetry,
2322 uint32_t inExpire,
2323 uint32_t inMinimumTTL,
2324 size_t * outLen );
2325 static OSStatus
2326 CreateSOARecordData(
2327 const uint8_t * inMName,
2328 const uint8_t * inRName,
2329 uint32_t inSerial,
2330 uint32_t inRefresh,
2331 uint32_t inRetry,
2332 uint32_t inExpire,
2333 uint32_t inMinimumTTL,
2334 uint8_t ** outPtr,
2335 size_t * outLen );
2336 static OSStatus
2337 _DataBuffer_AppendDNSQuestion(
2338 DataBuffer * inDB,
2339 const uint8_t * inNamePtr,
2340 size_t inNameLen,
2341 uint16_t inType,
2342 uint16_t inClass );
2343 static OSStatus
2344 _DataBuffer_AppendDNSRecord(
2345 DataBuffer * inDB,
2346 const uint8_t * inNamePtr,
2347 size_t inNameLen,
2348 uint16_t inType,
2349 uint16_t inClass,
2350 uint32_t inTTL,
2351 const uint8_t * inRDataPtr,
2352 size_t inRDataLen );
2353 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2354
2355 typedef struct MDNSInterfaceItem MDNSInterfaceItem;
2356 struct MDNSInterfaceItem
2357 {
2358 MDNSInterfaceItem * next;
2359 char * ifName;
2360 uint32_t ifIndex;
2361 Boolean hasIPv4;
2362 Boolean hasIPv6;
2363 Boolean isAWDL;
2364 Boolean isWiFi;
2365 };
2366
2367 typedef enum
2368 {
2369 kMDNSInterfaceSubset_All = 0, // All mDNS-capable interfaces.
2370 kMDNSInterfaceSubset_AWDL = 1, // All mDNS-capable AWDL interfaces.
2371 kMDNSInterfaceSubset_NonAWDL = 2 // All mDNS-capable non-AWDL iterfaces.
2372
2373 } MDNSInterfaceSubset;
2374
2375 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2376 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2377 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2378 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2379
2380 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2381 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName );
2382 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName );
2383 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2384
2385 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2386 static OSStatus
2387 _StringToIPv4Address(
2388 const char * inStr,
2389 StringToIPAddressFlags inFlags,
2390 uint32_t * outIP,
2391 int * outPort,
2392 uint32_t * outSubnet,
2393 uint32_t * outRouter,
2394 const char ** outStr );
2395 static void _StringArray_Free( char **inArray, size_t inCount );
2396 static OSStatus
2397 _StringToIPv6Address(
2398 const char * inStr,
2399 StringToIPAddressFlags inFlags,
2400 uint8_t outIPv6[ 16 ],
2401 uint32_t * outScope,
2402 int * outPort,
2403 int * outPrefix,
2404 const char ** outStr );
2405 static Boolean
2406 _ParseQuotedEscapedString(
2407 const char * inSrc,
2408 const char * inEnd,
2409 const char * inDelimiters,
2410 char * inBuf,
2411 size_t inMaxLen,
2412 size_t * outCopiedLen,
2413 size_t * outTotalLen,
2414 const char ** outSrc );
2415 static void * _memdup( const void *inPtr, size_t inLen );
2416 static int _memicmp( const void *inP1, const void *inP2, size_t inLen );
2417 static uint32_t _FNV1( const void *inData, size_t inSize );
2418
2419 #define Unused( X ) (void)(X)
2420
2421 //===========================================================================================================================
2422 // MDNSCollider
2423 //===========================================================================================================================
2424
2425 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2426
2427 typedef uint32_t MDNSColliderProtocols;
2428 #define kMDNSColliderProtocol_None 0
2429 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2430 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2431
2432 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2433
2434 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2435 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2436 static void MDNSColliderStop( MDNSColliderRef inCollider );
2437 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2438 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2439 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2440 static void
2441 MDNSColliderSetStopHandler(
2442 MDNSColliderRef inCollider,
2443 MDNSColliderStopHandler_f inStopHandler,
2444 void * inStopContext );
2445 static OSStatus
2446 MDNSColliderSetRecord(
2447 MDNSColliderRef inCollider,
2448 const uint8_t * inName,
2449 uint16_t inType,
2450 const void * inRDataPtr,
2451 size_t inRDataLen );
2452 static CFTypeID MDNSColliderGetTypeID( void );
2453
2454 //===========================================================================================================================
2455 // ServiceBrowser
2456 //===========================================================================================================================
2457
2458 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2459 typedef struct ServiceBrowserResults ServiceBrowserResults;
2460 typedef struct SBRDomain SBRDomain;
2461 typedef struct SBRServiceType SBRServiceType;
2462 typedef struct SBRServiceInstance SBRServiceInstance;
2463 typedef struct SBRIPAddress SBRIPAddress;
2464
2465 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2466
2467 struct ServiceBrowserResults
2468 {
2469 SBRDomain * domainList; // List of domains in which services were found.
2470 };
2471
2472 struct SBRDomain
2473 {
2474 SBRDomain * next; // Next domain in list.
2475 char * name; // Name of domain represented by this object.
2476 SBRServiceType * typeList; // List of service types in this domain.
2477 };
2478
2479 struct SBRServiceType
2480 {
2481 SBRServiceType * next; // Next service type in list.
2482 char * name; // Name of service type represented by this object.
2483 SBRServiceInstance * instanceList; // List of service instances of this service type.
2484 };
2485
2486 struct SBRServiceInstance
2487 {
2488 SBRServiceInstance * next; // Next service instance in list.
2489 char * name; // Name of service instance represented by this object.
2490 char * hostname; // Target from service instance's SRV record.
2491 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2492 uint16_t port; // Port from service instance's SRV record.
2493 uint8_t * txtPtr; // Service instance's TXT record data.
2494 size_t txtLen; // Service instance's TXT record data length.
2495 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2496 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2497 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2498 };
2499
2500 struct SBRIPAddress
2501 {
2502 SBRIPAddress * next; // Next IP address in list.
2503 sockaddr_ip sip; // IPv4 or IPv6 address.
2504 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2505 };
2506
2507 static CFTypeID ServiceBrowserGetTypeID( void );
2508 static OSStatus
2509 ServiceBrowserCreate(
2510 dispatch_queue_t inQueue,
2511 uint32_t inInterfaceIndex,
2512 const char * inDomain,
2513 unsigned int inBrowseTimeSecs,
2514 Boolean inIncludeAWDL,
2515 ServiceBrowserRef * outBrowser );
2516 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2517 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2518 static void
2519 ServiceBrowserSetCallback(
2520 ServiceBrowserRef inBrowser,
2521 ServiceBrowserCallback_f inCallback,
2522 void * inContext );
2523 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2524 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2525
2526 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2527
2528 //===========================================================================================================================
2529 // main
2530 //===========================================================================================================================
2531
2532 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2533 static int \
2534 _PrintFExtension ## NAME ## Handler( \
2535 PrintFContext * inContext, \
2536 PrintFFormat * inFormat, \
2537 PrintFVAList * inArgs, \
2538 void * inUserContext )
2539
2540 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
2541 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
2542 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
2543 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
2544
2545 int main( int argc, const char **argv )
2546 {
2547 OSStatus err;
2548
2549 // Route DebugServices logging output to stderr.
2550
2551 dlog_control( "DebugServices:output=file;stderr" );
2552
2553 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler, NULL );
2554 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler, NULL );
2555 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler, NULL );
2556 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler, NULL );
2557 CLIInit( argc, argv );
2558 err = CLIParse( kGlobalOpts, kCLIFlags_None );
2559 if( err ) exit( 1 );
2560
2561 return( gExitCode );
2562 }
2563
2564 //===========================================================================================================================
2565 // VersionOptionCallback
2566 //===========================================================================================================================
2567
2568 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
2569 {
2570 const char * srcVers;
2571 #if( MDNSRESPONDER_PROJECT )
2572 char srcStr[ 16 ];
2573 #endif
2574
2575 Unused( inOption );
2576 Unused( inArg );
2577 Unused( inUnset );
2578
2579 #if( MDNSRESPONDER_PROJECT )
2580 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
2581 #else
2582 srcVers = DNSSDUTIL_SOURCE_VERSION;
2583 #endif
2584 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
2585
2586 return( kEndingErr );
2587 }
2588
2589 //===========================================================================================================================
2590 // BrowseCmd
2591 //===========================================================================================================================
2592
2593 typedef struct BrowseResolveOp BrowseResolveOp;
2594
2595 struct BrowseResolveOp
2596 {
2597 BrowseResolveOp * next; // Next resolve operation in list.
2598 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2599 char * fullName; // Full name of the service to resolve.
2600 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2601 };
2602
2603 typedef struct
2604 {
2605 DNSServiceRef mainRef; // Main sdRef for shared connection.
2606 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
2607 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
2608 const char * domain; // Domain for DNSServiceBrowse operation(s).
2609 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
2610 char ** serviceTypes; // Array of service types to browse for.
2611 size_t serviceTypesCount; // Count of array of service types to browse for.
2612 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
2613 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
2614 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
2615 Boolean printedHeader; // True if results header has been printed.
2616 Boolean doResolve; // True if service instances are to be resolved.
2617 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
2618
2619 } BrowseContext;
2620
2621 static void BrowsePrintPrologue( const BrowseContext *inContext );
2622 static void BrowseContextFree( BrowseContext *inContext );
2623 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
2624 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
2625 static void DNSSD_API
2626 BrowseCallback(
2627 DNSServiceRef inSDRef,
2628 DNSServiceFlags inFlags,
2629 uint32_t inInterfaceIndex,
2630 DNSServiceErrorType inError,
2631 const char * inName,
2632 const char * inRegType,
2633 const char * inDomain,
2634 void * inContext );
2635 static void DNSSD_API
2636 BrowseResolveCallback(
2637 DNSServiceRef inSDRef,
2638 DNSServiceFlags inFlags,
2639 uint32_t inInterfaceIndex,
2640 DNSServiceErrorType inError,
2641 const char * inFullName,
2642 const char * inHostname,
2643 uint16_t inPort,
2644 uint16_t inTXTLen,
2645 const unsigned char * inTXTPtr,
2646 void * inContext );
2647 static void DNSSD_API
2648 BrowseQueryRecordCallback(
2649 DNSServiceRef inSDRef,
2650 DNSServiceFlags inFlags,
2651 uint32_t inInterfaceIndex,
2652 DNSServiceErrorType inError,
2653 const char * inFullName,
2654 uint16_t inType,
2655 uint16_t inClass,
2656 uint16_t inRDataLen,
2657 const void * inRDataPtr,
2658 uint32_t inTTL,
2659 void * inContext );
2660
2661 static void BrowseCmd( void )
2662 {
2663 OSStatus err;
2664 size_t i;
2665 BrowseContext * context = NULL;
2666 dispatch_source_t signalSource = NULL;
2667 int useMainConnection;
2668
2669 // Set up SIGINT handler.
2670
2671 signal( SIGINT, SIG_IGN );
2672 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2673 require_noerr( err, exit );
2674 dispatch_resume( signalSource );
2675
2676 // Create context.
2677
2678 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
2679 require_action( context, exit, err = kNoMemoryErr );
2680
2681 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
2682 require_action( context->opRefs, exit, err = kNoMemoryErr );
2683 context->opRefsCount = gBrowse_ServiceTypesCount;
2684
2685 // Check command parameters.
2686
2687 if( gBrowse_TimeLimitSecs < 0 )
2688 {
2689 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
2690 err = kParamErr;
2691 goto exit;
2692 }
2693
2694 // Create main connection.
2695
2696 if( gConnectionOpt )
2697 {
2698 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2699 require_noerr_quiet( err, exit );
2700 useMainConnection = true;
2701 }
2702 else
2703 {
2704 useMainConnection = false;
2705 }
2706
2707 // Get flags.
2708
2709 context->flags = GetDNSSDFlagsFromOpts();
2710 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2711
2712 // Get interface.
2713
2714 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2715 require_noerr_quiet( err, exit );
2716
2717 // Set remaining parameters.
2718
2719 context->serviceTypes = gBrowse_ServiceTypes;
2720 context->serviceTypesCount = gBrowse_ServiceTypesCount;
2721 context->domain = gBrowse_Domain;
2722 context->doResolve = gBrowse_DoResolve ? true : false;
2723 context->timeLimitSecs = gBrowse_TimeLimitSecs;
2724 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
2725
2726 // Print prologue.
2727
2728 BrowsePrintPrologue( context );
2729
2730 // Start operation(s).
2731
2732 for( i = 0; i < context->serviceTypesCount; ++i )
2733 {
2734 DNSServiceRef sdRef;
2735
2736 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2737 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
2738 BrowseCallback, context );
2739 require_noerr( err, exit );
2740
2741 context->opRefs[ i ] = sdRef;
2742 if( !useMainConnection )
2743 {
2744 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
2745 require_noerr( err, exit );
2746 }
2747 }
2748
2749 // Set time limit.
2750
2751 if( context->timeLimitSecs > 0 )
2752 {
2753 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2754 kExitReason_TimeLimit, Exit );
2755 }
2756 dispatch_main();
2757
2758 exit:
2759 dispatch_source_forget( &signalSource );
2760 if( context ) BrowseContextFree( context );
2761 if( err ) exit( 1 );
2762 }
2763
2764 //===========================================================================================================================
2765 // BrowsePrintPrologue
2766 //===========================================================================================================================
2767
2768 static void BrowsePrintPrologue( const BrowseContext *inContext )
2769 {
2770 const int timeLimitSecs = inContext->timeLimitSecs;
2771 const char * const * ptr = (const char **) inContext->serviceTypes;
2772 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
2773 char ifName[ kInterfaceNameBufLen ];
2774
2775 InterfaceIndexToName( inContext->ifIndex, ifName );
2776
2777 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2778 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2779 FPrintF( stdout, "Service types: %s", *ptr++ );
2780 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
2781 FPrintF( stdout, "\n" );
2782 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2783 FPrintF( stdout, "Time limit: " );
2784 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2785 else FPrintF( stdout, "∞\n" );
2786 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2787 FPrintF( stdout, "---\n" );
2788 }
2789
2790 //===========================================================================================================================
2791 // BrowseContextFree
2792 //===========================================================================================================================
2793
2794 static void BrowseContextFree( BrowseContext *inContext )
2795 {
2796 size_t i;
2797
2798 for( i = 0; i < inContext->opRefsCount; ++i )
2799 {
2800 DNSServiceForget( &inContext->opRefs[ i ] );
2801 }
2802 if( inContext->serviceTypes )
2803 {
2804 _StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
2805 inContext->serviceTypes = NULL;
2806 inContext->serviceTypesCount = 0;
2807 }
2808 DNSServiceForget( &inContext->mainRef );
2809 free( inContext );
2810 }
2811
2812 //===========================================================================================================================
2813 // BrowseResolveOpCreate
2814 //===========================================================================================================================
2815
2816 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
2817 {
2818 OSStatus err;
2819 BrowseResolveOp * resolveOp;
2820
2821 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
2822 require_action( resolveOp, exit, err = kNoMemoryErr );
2823
2824 resolveOp->fullName = strdup( inFullName );
2825 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
2826
2827 resolveOp->interfaceIndex = inInterfaceIndex;
2828
2829 *outOp = resolveOp;
2830 resolveOp = NULL;
2831 err = kNoErr;
2832
2833 exit:
2834 if( resolveOp ) BrowseResolveOpFree( resolveOp );
2835 return( err );
2836 }
2837
2838 //===========================================================================================================================
2839 // BrowseResolveOpFree
2840 //===========================================================================================================================
2841
2842 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
2843 {
2844 DNSServiceForget( &inOp->sdRef );
2845 ForgetMem( &inOp->fullName );
2846 free( inOp );
2847 }
2848
2849 //===========================================================================================================================
2850 // BrowseCallback
2851 //===========================================================================================================================
2852
2853 static void DNSSD_API
2854 BrowseCallback(
2855 DNSServiceRef inSDRef,
2856 DNSServiceFlags inFlags,
2857 uint32_t inInterfaceIndex,
2858 DNSServiceErrorType inError,
2859 const char * inName,
2860 const char * inRegType,
2861 const char * inDomain,
2862 void * inContext )
2863 {
2864 BrowseContext * const context = (BrowseContext *) inContext;
2865 OSStatus err;
2866 BrowseResolveOp * newOp = NULL;
2867 BrowseResolveOp ** p;
2868 char fullName[ kDNSServiceMaxDomainName ];
2869 struct timeval now;
2870
2871 Unused( inSDRef );
2872
2873 gettimeofday( &now, NULL );
2874
2875 err = inError;
2876 require_noerr( err, exit );
2877
2878 if( !context->printedHeader )
2879 {
2880 FPrintF( stdout, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2881 context->printedHeader = true;
2882 }
2883 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2884 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2885
2886 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2887
2888 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2889 require_noerr( err, exit );
2890
2891 if( inFlags & kDNSServiceFlagsAdd )
2892 {
2893 DNSServiceRef sdRef;
2894 DNSServiceFlags flags;
2895
2896 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2897 require_noerr( err, exit );
2898
2899 if( context->mainRef )
2900 {
2901 sdRef = context->mainRef;
2902 flags = kDNSServiceFlagsShareConnection;
2903 }
2904 else
2905 {
2906 flags = 0;
2907 }
2908 if( context->doResolve )
2909 {
2910 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2911 NULL );
2912 require_noerr( err, exit );
2913 }
2914 else
2915 {
2916 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2917 BrowseQueryRecordCallback, NULL );
2918 require_noerr( err, exit );
2919 }
2920
2921 newOp->sdRef = sdRef;
2922 if( !context->mainRef )
2923 {
2924 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2925 require_noerr( err, exit );
2926 }
2927 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2928 *p = newOp;
2929 newOp = NULL;
2930 }
2931 else
2932 {
2933 BrowseResolveOp * resolveOp;
2934
2935 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2936 {
2937 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
2938 {
2939 break;
2940 }
2941 }
2942 if( resolveOp )
2943 {
2944 *p = resolveOp->next;
2945 BrowseResolveOpFree( resolveOp );
2946 }
2947 }
2948
2949 exit:
2950 if( newOp ) BrowseResolveOpFree( newOp );
2951 if( err ) exit( 1 );
2952 }
2953
2954 //===========================================================================================================================
2955 // BrowseQueryRecordCallback
2956 //===========================================================================================================================
2957
2958 static void DNSSD_API
2959 BrowseQueryRecordCallback(
2960 DNSServiceRef inSDRef,
2961 DNSServiceFlags inFlags,
2962 uint32_t inInterfaceIndex,
2963 DNSServiceErrorType inError,
2964 const char * inFullName,
2965 uint16_t inType,
2966 uint16_t inClass,
2967 uint16_t inRDataLen,
2968 const void * inRDataPtr,
2969 uint32_t inTTL,
2970 void * inContext )
2971 {
2972 OSStatus err;
2973 struct timeval now;
2974
2975 Unused( inSDRef );
2976 Unused( inClass );
2977 Unused( inTTL );
2978 Unused( inContext );
2979
2980 gettimeofday( &now, NULL );
2981
2982 err = inError;
2983 require_noerr( err, exit );
2984 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
2985
2986 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2987 &now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
2988 (size_t) inRDataLen );
2989
2990 exit:
2991 if( err ) exit( 1 );
2992 }
2993
2994 //===========================================================================================================================
2995 // BrowseResolveCallback
2996 //===========================================================================================================================
2997
2998 static void DNSSD_API
2999 BrowseResolveCallback(
3000 DNSServiceRef inSDRef,
3001 DNSServiceFlags inFlags,
3002 uint32_t inInterfaceIndex,
3003 DNSServiceErrorType inError,
3004 const char * inFullName,
3005 const char * inHostname,
3006 uint16_t inPort,
3007 uint16_t inTXTLen,
3008 const unsigned char * inTXTPtr,
3009 void * inContext )
3010 {
3011 struct timeval now;
3012 char errorStr[ 64 ];
3013
3014 Unused( inSDRef );
3015 Unused( inFlags );
3016 Unused( inContext );
3017
3018 gettimeofday( &now, NULL );
3019
3020 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3021
3022 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3023 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3024 if( inTXTLen == 1 )
3025 {
3026 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3027 }
3028 else
3029 {
3030 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3031 }
3032 }
3033
3034 //===========================================================================================================================
3035 // GetAddrInfoCmd
3036 //===========================================================================================================================
3037
3038 typedef struct
3039 {
3040 DNSServiceRef mainRef; // Main sdRef for shared connection.
3041 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3042 const char * name; // Hostname to resolve.
3043 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3044 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3045 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3046 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3047 Boolean printedHeader; // True if the results header has been printed.
3048 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3049 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3050 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3051
3052 } GetAddrInfoContext;
3053
3054 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3055 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3056 static void DNSSD_API
3057 GetAddrInfoCallback(
3058 DNSServiceRef inSDRef,
3059 DNSServiceFlags inFlags,
3060 uint32_t inInterfaceIndex,
3061 DNSServiceErrorType inError,
3062 const char * inHostname,
3063 const struct sockaddr * inSockAddr,
3064 uint32_t inTTL,
3065 void * inContext );
3066
3067 static void GetAddrInfoCmd( void )
3068 {
3069 OSStatus err;
3070 DNSServiceRef sdRef;
3071 GetAddrInfoContext * context = NULL;
3072 dispatch_source_t signalSource = NULL;
3073 int useMainConnection;
3074
3075 // Set up SIGINT handler.
3076
3077 signal( SIGINT, SIG_IGN );
3078 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3079 require_noerr( err, exit );
3080 dispatch_resume( signalSource );
3081
3082 // Check command parameters.
3083
3084 if( gGetAddrInfo_TimeLimitSecs < 0 )
3085 {
3086 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3087 err = kParamErr;
3088 goto exit;
3089 }
3090
3091 // Create context.
3092
3093 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3094 require_action( context, exit, err = kNoMemoryErr );
3095
3096 // Create main connection.
3097
3098 if( gConnectionOpt )
3099 {
3100 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3101 require_noerr_quiet( err, exit );
3102 useMainConnection = true;
3103 }
3104 else
3105 {
3106 useMainConnection = false;
3107 }
3108
3109 // Get flags.
3110
3111 context->flags = GetDNSSDFlagsFromOpts();
3112 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3113
3114 // Get interface.
3115
3116 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3117 require_noerr_quiet( err, exit );
3118
3119 // Set remaining parameters.
3120
3121 context->name = gGetAddrInfo_Name;
3122 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3123 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3124 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3125 if( gGetAddrInfo_OneShot )
3126 {
3127 context->oneShotMode = true;
3128 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3129 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3130 }
3131
3132 // Print prologue.
3133
3134 GetAddrInfoPrintPrologue( context );
3135
3136 // Start operation.
3137
3138 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3139 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3140 GetAddrInfoCallback, context );
3141 require_noerr( err, exit );
3142
3143 context->opRef = sdRef;
3144 if( !useMainConnection )
3145 {
3146 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3147 require_noerr( err, exit );
3148 }
3149
3150 // Set time limit.
3151
3152 if( context->timeLimitSecs > 0 )
3153 {
3154 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3155 kExitReason_TimeLimit, Exit );
3156 }
3157 dispatch_main();
3158
3159 exit:
3160 dispatch_source_forget( &signalSource );
3161 if( context ) GetAddrInfoContextFree( context );
3162 if( err ) exit( 1 );
3163 }
3164
3165 //===========================================================================================================================
3166 // GetAddrInfoPrintPrologue
3167 //===========================================================================================================================
3168
3169 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3170 {
3171 const int timeLimitSecs = inContext->timeLimitSecs;
3172 char ifName[ kInterfaceNameBufLen ];
3173
3174 InterfaceIndexToName( inContext->ifIndex, ifName );
3175
3176 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3177 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3178 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3179 FPrintF( stdout, "Name: %s\n", inContext->name );
3180 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3181 FPrintF( stdout, "Time limit: " );
3182 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3183 else FPrintF( stdout, "∞\n" );
3184 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3185 FPrintF( stdout, "---\n" );
3186 }
3187
3188 //===========================================================================================================================
3189 // GetAddrInfoContextFree
3190 //===========================================================================================================================
3191
3192 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3193 {
3194 DNSServiceForget( &inContext->opRef );
3195 DNSServiceForget( &inContext->mainRef );
3196 free( inContext );
3197 }
3198
3199 //===========================================================================================================================
3200 // GetAddrInfoCallback
3201 //===========================================================================================================================
3202
3203 static void DNSSD_API
3204 GetAddrInfoCallback(
3205 DNSServiceRef inSDRef,
3206 DNSServiceFlags inFlags,
3207 uint32_t inInterfaceIndex,
3208 DNSServiceErrorType inError,
3209 const char * inHostname,
3210 const struct sockaddr * inSockAddr,
3211 uint32_t inTTL,
3212 void * inContext )
3213 {
3214 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3215 struct timeval now;
3216 OSStatus err;
3217 const char * addrStr;
3218 char addrStrBuf[ kSockAddrStringMaxSize ];
3219
3220 Unused( inSDRef );
3221
3222 gettimeofday( &now, NULL );
3223
3224 switch( inError )
3225 {
3226 case kDNSServiceErr_NoError:
3227 case kDNSServiceErr_NoSuchRecord:
3228 err = kNoErr;
3229 break;
3230
3231 case kDNSServiceErr_Timeout:
3232 Exit( kExitReason_Timeout );
3233
3234 default:
3235 err = inError;
3236 goto exit;
3237 }
3238
3239 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3240 {
3241 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3242 err = kTypeErr;
3243 goto exit;
3244 }
3245
3246 if( !inError )
3247 {
3248 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3249 require_noerr( err, exit );
3250 addrStr = addrStrBuf;
3251 }
3252 else
3253 {
3254 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3255 }
3256
3257 if( !context->printedHeader )
3258 {
3259 FPrintF( stdout, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3260 context->printedHeader = true;
3261 }
3262 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3263 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3264
3265 if( context->oneShotMode )
3266 {
3267 if( inFlags & kDNSServiceFlagsAdd )
3268 {
3269 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3270 else context->needIPv6 = false;
3271 }
3272 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3273 {
3274 Exit( kExitReason_OneShotDone );
3275 }
3276 }
3277
3278 exit:
3279 if( err ) exit( 1 );
3280 }
3281
3282 //===========================================================================================================================
3283 // QueryRecordCmd
3284 //===========================================================================================================================
3285
3286 typedef struct
3287 {
3288 DNSServiceRef mainRef; // Main sdRef for shared connection.
3289 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3290 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3291 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3292 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3293 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3294 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3295 Boolean printedHeader; // True if the results header was printed.
3296 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3297 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3298 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3299
3300 } QueryRecordContext;
3301
3302 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3303 static void QueryRecordContextFree( QueryRecordContext *inContext );
3304 static void DNSSD_API
3305 QueryRecordCallback(
3306 DNSServiceRef inSDRef,
3307 DNSServiceFlags inFlags,
3308 uint32_t inInterfaceIndex,
3309 DNSServiceErrorType inError,
3310 const char * inFullName,
3311 uint16_t inType,
3312 uint16_t inClass,
3313 uint16_t inRDataLen,
3314 const void * inRDataPtr,
3315 uint32_t inTTL,
3316 void * inContext );
3317
3318 static void QueryRecordCmd( void )
3319 {
3320 OSStatus err;
3321 DNSServiceRef sdRef;
3322 QueryRecordContext * context = NULL;
3323 dispatch_source_t signalSource = NULL;
3324 int useMainConnection;
3325
3326 // Set up SIGINT handler.
3327
3328 signal( SIGINT, SIG_IGN );
3329 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3330 require_noerr( err, exit );
3331 dispatch_resume( signalSource );
3332
3333 // Create context.
3334
3335 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3336 require_action( context, exit, err = kNoMemoryErr );
3337
3338 // Check command parameters.
3339
3340 if( gQueryRecord_TimeLimitSecs < 0 )
3341 {
3342 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3343 err = kParamErr;
3344 goto exit;
3345 }
3346
3347 // Create main connection.
3348
3349 if( gConnectionOpt )
3350 {
3351 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3352 require_noerr_quiet( err, exit );
3353 useMainConnection = true;
3354 }
3355 else
3356 {
3357 useMainConnection = false;
3358 }
3359
3360 // Get flags.
3361
3362 context->flags = GetDNSSDFlagsFromOpts();
3363 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3364
3365 // Get interface.
3366
3367 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3368 require_noerr_quiet( err, exit );
3369
3370 // Get record type.
3371
3372 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3373 require_noerr( err, exit );
3374
3375 // Set remaining parameters.
3376
3377 context->recordName = gQueryRecord_Name;
3378 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3379 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3380 context->printRawRData = gQueryRecord_RawRData ? true : false;
3381
3382 // Print prologue.
3383
3384 QueryRecordPrintPrologue( context );
3385
3386 // Start operation.
3387
3388 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3389 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3390 kDNSServiceClass_IN, QueryRecordCallback, context );
3391 require_noerr( err, exit );
3392
3393 context->opRef = sdRef;
3394 if( !useMainConnection )
3395 {
3396 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3397 require_noerr( err, exit );
3398 }
3399
3400 // Set time limit.
3401
3402 if( context->timeLimitSecs > 0 )
3403 {
3404 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3405 Exit );
3406 }
3407 dispatch_main();
3408
3409 exit:
3410 dispatch_source_forget( &signalSource );
3411 if( context ) QueryRecordContextFree( context );
3412 if( err ) exit( 1 );
3413 }
3414
3415 //===========================================================================================================================
3416 // QueryRecordContextFree
3417 //===========================================================================================================================
3418
3419 static void QueryRecordContextFree( QueryRecordContext *inContext )
3420 {
3421 DNSServiceForget( &inContext->opRef );
3422 DNSServiceForget( &inContext->mainRef );
3423 free( inContext );
3424 }
3425
3426 //===========================================================================================================================
3427 // QueryRecordPrintPrologue
3428 //===========================================================================================================================
3429
3430 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3431 {
3432 const int timeLimitSecs = inContext->timeLimitSecs;
3433 char ifName[ kInterfaceNameBufLen ];
3434
3435 InterfaceIndexToName( inContext->ifIndex, ifName );
3436
3437 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3438 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3439 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3440 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3441 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3442 FPrintF( stdout, "Time limit: " );
3443 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3444 else FPrintF( stdout, "∞\n" );
3445 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3446 FPrintF( stdout, "---\n" );
3447
3448 }
3449
3450 //===========================================================================================================================
3451 // QueryRecordCallback
3452 //===========================================================================================================================
3453
3454 static void DNSSD_API
3455 QueryRecordCallback(
3456 DNSServiceRef inSDRef,
3457 DNSServiceFlags inFlags,
3458 uint32_t inInterfaceIndex,
3459 DNSServiceErrorType inError,
3460 const char * inFullName,
3461 uint16_t inType,
3462 uint16_t inClass,
3463 uint16_t inRDataLen,
3464 const void * inRDataPtr,
3465 uint32_t inTTL,
3466 void * inContext )
3467 {
3468 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3469 struct timeval now;
3470 OSStatus err;
3471 char * rdataStr = NULL;
3472
3473 Unused( inSDRef );
3474
3475 gettimeofday( &now, NULL );
3476
3477 switch( inError )
3478 {
3479 case kDNSServiceErr_NoError:
3480 case kDNSServiceErr_NoSuchRecord:
3481 err = kNoErr;
3482 break;
3483
3484 case kDNSServiceErr_Timeout:
3485 Exit( kExitReason_Timeout );
3486
3487 default:
3488 err = inError;
3489 goto exit;
3490 }
3491
3492 if( inError != kDNSServiceErr_NoSuchRecord )
3493 {
3494 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
3495 if( !rdataStr )
3496 {
3497 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
3498 require_action( rdataStr, exit, err = kNoMemoryErr );
3499 }
3500 }
3501
3502 if( !context->printedHeader )
3503 {
3504 FPrintF( stdout, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3505 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3506 context->printedHeader = true;
3507 }
3508 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3509 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
3510 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL,
3511 rdataStr ? rdataStr : kNoSuchRecordStr );
3512
3513 if( context->oneShotMode )
3514 {
3515 if( ( inFlags & kDNSServiceFlagsAdd ) &&
3516 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
3517 {
3518 context->gotRecord = true;
3519 }
3520 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
3521 }
3522
3523 exit:
3524 FreeNullSafe( rdataStr );
3525 if( err ) exit( 1 );
3526 }
3527
3528 //===========================================================================================================================
3529 // RegisterCmd
3530 //===========================================================================================================================
3531
3532 typedef struct
3533 {
3534 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
3535 uint8_t * dataPtr; // Record data.
3536 size_t dataLen; // Record data length.
3537 uint32_t ttl; // Record TTL value.
3538 uint16_t type; // Record type.
3539
3540 } ExtraRecord;
3541
3542 typedef struct
3543 {
3544 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
3545 const char * name; // Service name argument for DNSServiceRegister().
3546 const char * type; // Service type argument for DNSServiceRegister().
3547 const char * domain; // Domain in which advertise the service.
3548 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
3549 size_t txtLen; // Service TXT record data len.
3550 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
3551 size_t extraRecordsCount; // Number of extra records.
3552 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
3553 size_t updateTXTLen; // Length of record data for TXT record update.
3554 uint32_t updateTTL; // TTL of updated TXT record.
3555 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
3556 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
3557 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
3558 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3559 uint16_t port; // Service instance's port number.
3560 Boolean printedHeader; // True if results header was printed.
3561 Boolean didRegister; // True if service was registered.
3562
3563 } RegisterContext;
3564
3565 static void RegisterPrintPrologue( const RegisterContext *inContext );
3566 static void RegisterContextFree( RegisterContext *inContext );
3567 static void DNSSD_API
3568 RegisterCallback(
3569 DNSServiceRef inSDRef,
3570 DNSServiceFlags inFlags,
3571 DNSServiceErrorType inError,
3572 const char * inName,
3573 const char * inType,
3574 const char * inDomain,
3575 void * inContext );
3576 static void RegisterUpdate( void *inContext );
3577
3578 static void RegisterCmd( void )
3579 {
3580 OSStatus err;
3581 RegisterContext * context = NULL;
3582 dispatch_source_t signalSource = NULL;
3583
3584 // Set up SIGINT handler.
3585
3586 signal( SIGINT, SIG_IGN );
3587 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3588 require_noerr( err, exit );
3589 dispatch_resume( signalSource );
3590
3591 // Create context.
3592
3593 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
3594 require_action( context, exit, err = kNoMemoryErr );
3595
3596 // Check command parameters.
3597
3598 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
3599 {
3600 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
3601 err = kParamErr;
3602 goto exit;
3603 }
3604
3605 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
3606 {
3607 FPrintF( stderr, "There are missing additional record parameters.\n" );
3608 err = kParamErr;
3609 goto exit;
3610 }
3611
3612 // Get flags.
3613
3614 context->flags = GetDNSSDFlagsFromOpts();
3615
3616 // Get interface index.
3617
3618 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3619 require_noerr_quiet( err, exit );
3620
3621 // Get TXT record data.
3622
3623 if( gRegister_TXT )
3624 {
3625 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
3626 require_noerr_quiet( err, exit );
3627 }
3628
3629 // Set remaining parameters.
3630
3631 context->name = gRegister_Name;
3632 context->type = gRegister_Type;
3633 context->domain = gRegister_Domain;
3634 context->port = (uint16_t) gRegister_Port;
3635 context->lifetimeMs = gRegister_LifetimeMs;
3636
3637 if( gAddRecord_TypesCount > 0 )
3638 {
3639 size_t i;
3640
3641 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
3642 require_action( context, exit, err = kNoMemoryErr );
3643 context->extraRecordsCount = gAddRecord_TypesCount;
3644
3645 for( i = 0; i < gAddRecord_TypesCount; ++i )
3646 {
3647 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
3648
3649 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
3650 require_noerr( err, exit );
3651
3652 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
3653 if( err )
3654 {
3655 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
3656 err = kParamErr;
3657 goto exit;
3658 }
3659
3660 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
3661 require_noerr_quiet( err, exit );
3662 }
3663 }
3664
3665 if( gUpdateRecord_Data )
3666 {
3667 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
3668 require_noerr_quiet( err, exit );
3669
3670 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
3671 context->updateDelayMs = gUpdateRecord_DelayMs;
3672 }
3673
3674 // Print prologue.
3675
3676 RegisterPrintPrologue( context );
3677
3678 // Start operation.
3679
3680 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
3681 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
3682 RegisterCallback, context );
3683 ForgetMem( &context->txtPtr );
3684 require_noerr( err, exit );
3685
3686 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3687 require_noerr( err, exit );
3688
3689 dispatch_main();
3690
3691 exit:
3692 dispatch_source_forget( &signalSource );
3693 if( context ) RegisterContextFree( context );
3694 if( err ) exit( 1 );
3695 }
3696
3697 //===========================================================================================================================
3698 // RegisterPrintPrologue
3699 //===========================================================================================================================
3700
3701 static void RegisterPrintPrologue( const RegisterContext *inContext )
3702 {
3703 size_t i;
3704 int infinite;
3705 char ifName[ kInterfaceNameBufLen ];
3706
3707 InterfaceIndexToName( inContext->ifIndex, ifName );
3708
3709 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3710 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3711 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
3712 FPrintF( stdout, "Type: %s\n", inContext->type );
3713 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3714 FPrintF( stdout, "Port: %u\n", inContext->port );
3715 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
3716 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3717 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3718 if( inContext->updateTXTPtr )
3719 {
3720 FPrintF( stdout, "\nUpdate record:\n" );
3721 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
3722 FPrintF( stdout, " TTL: %u%?s\n",
3723 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3724 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
3725 }
3726 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
3727 for( i = 0; i < inContext->extraRecordsCount; ++i )
3728 {
3729 const ExtraRecord * record = &inContext->extraRecords[ i ];
3730
3731 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
3732 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
3733 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
3734 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
3735 }
3736 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3737 FPrintF( stdout, "---\n" );
3738 }
3739
3740 //===========================================================================================================================
3741 // RegisterContextFree
3742 //===========================================================================================================================
3743
3744 static void RegisterContextFree( RegisterContext *inContext )
3745 {
3746 ExtraRecord * record;
3747 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
3748
3749 DNSServiceForget( &inContext->opRef );
3750 ForgetMem( &inContext->txtPtr );
3751 for( record = inContext->extraRecords; record < end; ++record )
3752 {
3753 check( !record->recordRef );
3754 ForgetMem( &record->dataPtr );
3755 }
3756 ForgetMem( &inContext->extraRecords );
3757 ForgetMem( &inContext->updateTXTPtr );
3758 free( inContext );
3759 }
3760
3761 //===========================================================================================================================
3762 // RegisterCallback
3763 //===========================================================================================================================
3764
3765 static void DNSSD_API
3766 RegisterCallback(
3767 DNSServiceRef inSDRef,
3768 DNSServiceFlags inFlags,
3769 DNSServiceErrorType inError,
3770 const char * inName,
3771 const char * inType,
3772 const char * inDomain,
3773 void * inContext )
3774 {
3775 RegisterContext * const context = (RegisterContext *) inContext;
3776 OSStatus err;
3777 struct timeval now;
3778
3779 Unused( inSDRef );
3780
3781 gettimeofday( &now, NULL );
3782
3783 if( !context->printedHeader )
3784 {
3785 FPrintF( stdout, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3786 context->printedHeader = true;
3787 }
3788 FPrintF( stdout, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
3789
3790 require_noerr_action_quiet( inError, exit, err = inError );
3791
3792 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
3793 {
3794 context->didRegister = true;
3795 if( context->updateTXTPtr )
3796 {
3797 if( context->updateDelayMs > 0 )
3798 {
3799 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3800 context, RegisterUpdate );
3801 }
3802 else
3803 {
3804 RegisterUpdate( context );
3805 }
3806 }
3807 if( context->extraRecordsCount > 0 )
3808 {
3809 ExtraRecord * record;
3810 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
3811
3812 for( record = context->extraRecords; record < end; ++record )
3813 {
3814 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
3815 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
3816 require_noerr( err, exit );
3817 }
3818 }
3819 if( context->lifetimeMs == 0 )
3820 {
3821 Exit( kExitReason_TimeLimit );
3822 }
3823 else if( context->lifetimeMs > 0 )
3824 {
3825 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3826 kExitReason_TimeLimit, Exit );
3827 }
3828 }
3829 err = kNoErr;
3830
3831 exit:
3832 if( err ) exit( 1 );
3833 }
3834
3835 //===========================================================================================================================
3836 // RegisterUpdate
3837 //===========================================================================================================================
3838
3839 static void RegisterUpdate( void *inContext )
3840 {
3841 OSStatus err;
3842 RegisterContext * const context = (RegisterContext *) inContext;
3843
3844 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
3845 context->updateTTL );
3846 require_noerr( err, exit );
3847
3848 exit:
3849 if( err ) exit( 1 );
3850 }
3851
3852 //===========================================================================================================================
3853 // RegisterRecordCmd
3854 //===========================================================================================================================
3855
3856 typedef struct
3857 {
3858 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
3859 DNSRecordRef recordRef; // Registered record reference.
3860 const char * recordName; // Name of resource record.
3861 uint8_t * dataPtr; // Pointer to resource record data.
3862 size_t dataLen; // Length of resource record data.
3863 uint32_t ttl; // TTL value of resource record in seconds.
3864 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3865 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3866 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3867 uint16_t recordType; // Resource record type.
3868 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3869 size_t updateDataLen; // Length of data for record update.
3870 uint32_t updateTTL; // TTL for updated record.
3871 int updateDelayMs; // Post-registration record update delay in milliseconds.
3872 Boolean didRegister; // True if the record was registered.
3873
3874 } RegisterRecordContext;
3875
3876 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3877 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3878 static void DNSSD_API
3879 RegisterRecordCallback(
3880 DNSServiceRef inSDRef,
3881 DNSRecordRef inRecordRef,
3882 DNSServiceFlags inFlags,
3883 DNSServiceErrorType inError,
3884 void * inContext );
3885 static void RegisterRecordUpdate( void *inContext );
3886
3887 static void RegisterRecordCmd( void )
3888 {
3889 OSStatus err;
3890 RegisterRecordContext * context = NULL;
3891 dispatch_source_t signalSource = NULL;
3892
3893 // Set up SIGINT handler.
3894
3895 signal( SIGINT, SIG_IGN );
3896 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3897 require_noerr( err, exit );
3898 dispatch_resume( signalSource );
3899
3900 // Create context.
3901
3902 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3903 require_action( context, exit, err = kNoMemoryErr );
3904
3905 // Create connection.
3906
3907 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3908 require_noerr_quiet( err, exit );
3909
3910 // Get flags.
3911
3912 context->flags = GetDNSSDFlagsFromOpts();
3913
3914 // Get interface.
3915
3916 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3917 require_noerr_quiet( err, exit );
3918
3919 // Get record type.
3920
3921 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3922 require_noerr( err, exit );
3923
3924 // Get record data.
3925
3926 if( gRegisterRecord_Data )
3927 {
3928 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3929 require_noerr_quiet( err, exit );
3930 }
3931
3932 // Set remaining parameters.
3933
3934 context->recordName = gRegisterRecord_Name;
3935 context->ttl = (uint32_t) gRegisterRecord_TTL;
3936 context->lifetimeMs = gRegisterRecord_LifetimeMs;
3937
3938 // Get update data.
3939
3940 if( gRegisterRecord_UpdateData )
3941 {
3942 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
3943 require_noerr_quiet( err, exit );
3944
3945 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
3946 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
3947 }
3948
3949 // Print prologue.
3950
3951 RegisterRecordPrintPrologue( context );
3952
3953 // Start operation.
3954
3955 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
3956 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
3957 context->ttl, RegisterRecordCallback, context );
3958 if( err )
3959 {
3960 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
3961 goto exit;
3962 }
3963
3964 dispatch_main();
3965
3966 exit:
3967 dispatch_source_forget( &signalSource );
3968 if( context ) RegisterRecordContextFree( context );
3969 if( err ) exit( 1 );
3970 }
3971
3972 //===========================================================================================================================
3973 // RegisterRecordPrintPrologue
3974 //===========================================================================================================================
3975
3976 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
3977 {
3978 int infinite;
3979 char ifName[ kInterfaceNameBufLen ];
3980
3981 InterfaceIndexToName( inContext->ifIndex, ifName );
3982
3983 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3984 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3985 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3986 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3987 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
3988 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
3989 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3990 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3991 if( inContext->updateDataPtr )
3992 {
3993 FPrintF( stdout, "\nUpdate record:\n" );
3994 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
3995 FPrintF( stdout, " TTL: %u%?s\n",
3996 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3997 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
3998 }
3999 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4000 FPrintF( stdout, "---\n" );
4001 }
4002
4003 //===========================================================================================================================
4004 // RegisterRecordContextFree
4005 //===========================================================================================================================
4006
4007 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
4008 {
4009 DNSServiceForget( &inContext->conRef );
4010 ForgetMem( &inContext->dataPtr );
4011 ForgetMem( &inContext->updateDataPtr );
4012 free( inContext );
4013 }
4014
4015 //===========================================================================================================================
4016 // RegisterRecordCallback
4017 //===========================================================================================================================
4018
4019 static void
4020 RegisterRecordCallback(
4021 DNSServiceRef inSDRef,
4022 DNSRecordRef inRecordRef,
4023 DNSServiceFlags inFlags,
4024 DNSServiceErrorType inError,
4025 void * inContext )
4026 {
4027 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4028 struct timeval now;
4029
4030 Unused( inSDRef );
4031 Unused( inRecordRef );
4032 Unused( inFlags );
4033 Unused( context );
4034
4035 gettimeofday( &now, NULL );
4036 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4037
4038 if( !context->didRegister && !inError )
4039 {
4040 context->didRegister = true;
4041 if( context->updateDataPtr )
4042 {
4043 if( context->updateDelayMs > 0 )
4044 {
4045 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4046 context, RegisterRecordUpdate );
4047 }
4048 else
4049 {
4050 RegisterRecordUpdate( context );
4051 }
4052 }
4053 if( context->lifetimeMs == 0 )
4054 {
4055 Exit( kExitReason_TimeLimit );
4056 }
4057 else if( context->lifetimeMs > 0 )
4058 {
4059 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4060 kExitReason_TimeLimit, Exit );
4061 }
4062 }
4063 }
4064
4065 //===========================================================================================================================
4066 // RegisterRecordUpdate
4067 //===========================================================================================================================
4068
4069 static void RegisterRecordUpdate( void *inContext )
4070 {
4071 OSStatus err;
4072 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4073
4074 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4075 context->updateDataPtr, context->updateTTL );
4076 require_noerr( err, exit );
4077
4078 exit:
4079 if( err ) exit( 1 );
4080 }
4081
4082 //===========================================================================================================================
4083 // ResolveCmd
4084 //===========================================================================================================================
4085
4086 typedef struct
4087 {
4088 DNSServiceRef mainRef; // Main sdRef for shared connections.
4089 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4090 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4091 const char * name; // Service name argument for DNSServiceResolve().
4092 const char * type; // Service type argument for DNSServiceResolve().
4093 const char * domain; // Domain argument for DNSServiceResolve().
4094 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4095 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4096
4097 } ResolveContext;
4098
4099 static void ResolvePrintPrologue( const ResolveContext *inContext );
4100 static void ResolveContextFree( ResolveContext *inContext );
4101 static void DNSSD_API
4102 ResolveCallback(
4103 DNSServiceRef inSDRef,
4104 DNSServiceFlags inFlags,
4105 uint32_t inInterfaceIndex,
4106 DNSServiceErrorType inError,
4107 const char * inFullName,
4108 const char * inHostname,
4109 uint16_t inPort,
4110 uint16_t inTXTLen,
4111 const unsigned char * inTXTPtr,
4112 void * inContext );
4113
4114 static void ResolveCmd( void )
4115 {
4116 OSStatus err;
4117 DNSServiceRef sdRef;
4118 ResolveContext * context = NULL;
4119 dispatch_source_t signalSource = NULL;
4120 int useMainConnection;
4121
4122 // Set up SIGINT handler.
4123
4124 signal( SIGINT, SIG_IGN );
4125 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4126 require_noerr( err, exit );
4127 dispatch_resume( signalSource );
4128
4129 // Create context.
4130
4131 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4132 require_action( context, exit, err = kNoMemoryErr );
4133
4134 // Check command parameters.
4135
4136 if( gResolve_TimeLimitSecs < 0 )
4137 {
4138 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4139 err = kParamErr;
4140 goto exit;
4141 }
4142
4143 // Create main connection.
4144
4145 if( gConnectionOpt )
4146 {
4147 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4148 require_noerr_quiet( err, exit );
4149 useMainConnection = true;
4150 }
4151 else
4152 {
4153 useMainConnection = false;
4154 }
4155
4156 // Get flags.
4157
4158 context->flags = GetDNSSDFlagsFromOpts();
4159 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4160
4161 // Get interface index.
4162
4163 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4164 require_noerr_quiet( err, exit );
4165
4166 // Set remaining parameters.
4167
4168 context->name = gResolve_Name;
4169 context->type = gResolve_Type;
4170 context->domain = gResolve_Domain;
4171 context->timeLimitSecs = gResolve_TimeLimitSecs;
4172
4173 // Print prologue.
4174
4175 ResolvePrintPrologue( context );
4176
4177 // Start operation.
4178
4179 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4180 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4181 ResolveCallback, NULL );
4182 require_noerr( err, exit );
4183
4184 context->opRef = sdRef;
4185 if( !useMainConnection )
4186 {
4187 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4188 require_noerr( err, exit );
4189 }
4190
4191 // Set time limit.
4192
4193 if( context->timeLimitSecs > 0 )
4194 {
4195 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4196 kExitReason_TimeLimit, Exit );
4197 }
4198 dispatch_main();
4199
4200 exit:
4201 dispatch_source_forget( &signalSource );
4202 if( context ) ResolveContextFree( context );
4203 if( err ) exit( 1 );
4204 }
4205
4206 //===========================================================================================================================
4207 // ReconfirmCmd
4208 //===========================================================================================================================
4209
4210 static void ReconfirmCmd( void )
4211 {
4212 OSStatus err;
4213 uint8_t * rdataPtr = NULL;
4214 size_t rdataLen = 0;
4215 DNSServiceFlags flags;
4216 uint32_t ifIndex;
4217 uint16_t type, class;
4218 char ifName[ kInterfaceNameBufLen ];
4219
4220 // Get flags.
4221
4222 flags = GetDNSSDFlagsFromOpts();
4223
4224 // Get interface index.
4225
4226 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4227 require_noerr_quiet( err, exit );
4228
4229 // Get record type.
4230
4231 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4232 require_noerr( err, exit );
4233
4234 // Get record data.
4235
4236 if( gReconfirmRecord_Data )
4237 {
4238 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4239 require_noerr_quiet( err, exit );
4240 }
4241
4242 // Get record class.
4243
4244 if( gReconfirmRecord_Class )
4245 {
4246 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4247 require_noerr( err, exit );
4248 }
4249 else
4250 {
4251 class = kDNSServiceClass_IN;
4252 }
4253
4254 // Print prologue.
4255
4256 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4257 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4258 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4259 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4260 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4261 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4262 FPrintF( stdout, "---\n" );
4263
4264 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4265 FPrintF( stdout, "Error: %#m\n", err );
4266
4267 exit:
4268 FreeNullSafe( rdataPtr );
4269 if( err ) exit( 1 );
4270 }
4271
4272 //===========================================================================================================================
4273 // ResolvePrintPrologue
4274 //===========================================================================================================================
4275
4276 static void ResolvePrintPrologue( const ResolveContext *inContext )
4277 {
4278 const int timeLimitSecs = inContext->timeLimitSecs;
4279 char ifName[ kInterfaceNameBufLen ];
4280
4281 InterfaceIndexToName( inContext->ifIndex, ifName );
4282
4283 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4284 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4285 FPrintF( stdout, "Name: %s\n", inContext->name );
4286 FPrintF( stdout, "Type: %s\n", inContext->type );
4287 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4288 FPrintF( stdout, "Time limit: " );
4289 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4290 else FPrintF( stdout, "∞\n" );
4291 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4292 FPrintF( stdout, "---\n" );
4293 }
4294
4295 //===========================================================================================================================
4296 // ResolveContextFree
4297 //===========================================================================================================================
4298
4299 static void ResolveContextFree( ResolveContext *inContext )
4300 {
4301 DNSServiceForget( &inContext->opRef );
4302 DNSServiceForget( &inContext->mainRef );
4303 free( inContext );
4304 }
4305
4306 //===========================================================================================================================
4307 // ResolveCallback
4308 //===========================================================================================================================
4309
4310 static void DNSSD_API
4311 ResolveCallback(
4312 DNSServiceRef inSDRef,
4313 DNSServiceFlags inFlags,
4314 uint32_t inInterfaceIndex,
4315 DNSServiceErrorType inError,
4316 const char * inFullName,
4317 const char * inHostname,
4318 uint16_t inPort,
4319 uint16_t inTXTLen,
4320 const unsigned char * inTXTPtr,
4321 void * inContext )
4322 {
4323 struct timeval now;
4324 char errorStr[ 64 ];
4325
4326 Unused( inSDRef );
4327 Unused( inFlags );
4328 Unused( inContext );
4329
4330 gettimeofday( &now, NULL );
4331
4332 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4333
4334 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4335 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4336 if( inTXTLen == 1 )
4337 {
4338 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4339 }
4340 else
4341 {
4342 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4343 }
4344 }
4345
4346 //===========================================================================================================================
4347 // GetAddrInfoPOSIXCmd
4348 //===========================================================================================================================
4349
4350 #define AddressFamilyStr( X ) ( \
4351 ( (X) == AF_INET ) ? "inet" : \
4352 ( (X) == AF_INET6 ) ? "inet6" : \
4353 ( (X) == AF_UNSPEC ) ? "unspec" : \
4354 "???" )
4355
4356 typedef struct
4357 {
4358 unsigned int flag;
4359 const char * str;
4360
4361 } FlagStringPair;
4362
4363 #define CaseFlagStringify( X ) { (X), # X }
4364
4365 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4366 {
4367 #if( defined( AI_UNUSABLE ) )
4368 CaseFlagStringify( AI_UNUSABLE ),
4369 #endif
4370 CaseFlagStringify( AI_NUMERICSERV ),
4371 CaseFlagStringify( AI_V4MAPPED ),
4372 CaseFlagStringify( AI_ADDRCONFIG ),
4373 #if( defined( AI_V4MAPPED_CFG ) )
4374 CaseFlagStringify( AI_V4MAPPED_CFG ),
4375 #endif
4376 CaseFlagStringify( AI_ALL ),
4377 CaseFlagStringify( AI_NUMERICHOST ),
4378 CaseFlagStringify( AI_CANONNAME ),
4379 CaseFlagStringify( AI_PASSIVE ),
4380 { 0, NULL }
4381 };
4382
4383 static void GetAddrInfoPOSIXCmd( void )
4384 {
4385 OSStatus err;
4386 struct addrinfo hints;
4387 struct timeval now;
4388 const struct addrinfo * addrInfo;
4389 struct addrinfo * addrInfoList = NULL;
4390 const FlagStringPair * pair;
4391
4392 memset( &hints, 0, sizeof( hints ) );
4393 hints.ai_socktype = SOCK_STREAM;
4394
4395 // Set hints address family.
4396
4397 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4398 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4399 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4400 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4401 else
4402 {
4403 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4404 err = kParamErr;
4405 goto exit;
4406 }
4407
4408 // Set hints flags.
4409
4410 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4411 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4412 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4413 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4414 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4415 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4416 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4417 #if( defined( AI_V4MAPPED_CFG ) )
4418 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4419 #endif
4420 #if( defined( AI_DEFAULT ) )
4421 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4422 #endif
4423 #if( defined( AI_UNUSABLE ) )
4424 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4425 #endif
4426
4427 // Print prologue.
4428
4429 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4430 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4431 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4432 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4433 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4434 {
4435 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4436 }
4437 FPrintF( stdout, ">\n" );
4438 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4439 FPrintF( stdout, "---\n" );
4440
4441 // Call getaddrinfo().
4442
4443 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4444 gettimeofday( &now, NULL );
4445 if( err )
4446 {
4447 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4448 }
4449 else
4450 {
4451 int addrCount = 0;
4452
4453 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4454
4455 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4456 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4457 {
4458 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4459 }
4460 }
4461 FPrintF( stdout, "---\n" );
4462 FPrintF( stdout, "End time: %{du:time}\n", &now );
4463
4464 exit:
4465 if( addrInfoList ) freeaddrinfo( addrInfoList );
4466 if( err ) exit( 1 );
4467 }
4468
4469 //===========================================================================================================================
4470 // ReverseLookupCmd
4471 //===========================================================================================================================
4472
4473 #define kIP6ARPADomainStr "ip6.arpa."
4474
4475 static void ReverseLookupCmd( void )
4476 {
4477 OSStatus err;
4478 QueryRecordContext * context = NULL;
4479 DNSServiceRef sdRef;
4480 dispatch_source_t signalSource = NULL;
4481 uint32_t ipv4Addr;
4482 uint8_t ipv6Addr[ 16 ];
4483 char recordName[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr ) ];
4484 int useMainConnection;
4485 const char * endPtr;
4486
4487 // Set up SIGINT handler.
4488
4489 signal( SIGINT, SIG_IGN );
4490 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4491 require_noerr( err, exit );
4492 dispatch_resume( signalSource );
4493
4494 // Create context.
4495
4496 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
4497 require_action( context, exit, err = kNoMemoryErr );
4498
4499 // Check command parameters.
4500
4501 if( gReverseLookup_TimeLimitSecs < 0 )
4502 {
4503 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
4504 err = kParamErr;
4505 goto exit;
4506 }
4507
4508 // Create main connection.
4509
4510 if( gConnectionOpt )
4511 {
4512 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4513 require_noerr_quiet( err, exit );
4514 useMainConnection = true;
4515 }
4516 else
4517 {
4518 useMainConnection = false;
4519 }
4520
4521 // Get flags.
4522
4523 context->flags = GetDNSSDFlagsFromOpts();
4524 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4525
4526 // Get interface index.
4527
4528 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4529 require_noerr_quiet( err, exit );
4530
4531 // Create reverse lookup record name.
4532
4533 err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
4534 &ipv4Addr, NULL, NULL, NULL, &endPtr );
4535 if( err || ( *endPtr != '\0' ) )
4536 {
4537 char * dst;
4538 int i;
4539
4540 err = _StringToIPv6Address( gReverseLookup_IPAddr,
4541 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
4542 ipv6Addr, NULL, NULL, NULL, &endPtr );
4543 if( err || ( *endPtr != '\0' ) )
4544 {
4545 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
4546 err = kParamErr;
4547 goto exit;
4548 }
4549 dst = recordName;
4550 for( i = 15; i >= 0; --i )
4551 {
4552 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
4553 *dst++ = '.';
4554 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
4555 *dst++ = '.';
4556 }
4557 strcpy_literal( dst, kIP6ARPADomainStr );
4558 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
4559 }
4560 else
4561 {
4562 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
4563 ipv4Addr & 0xFF,
4564 ( ipv4Addr >> 8 ) & 0xFF,
4565 ( ipv4Addr >> 16 ) & 0xFF,
4566 ( ipv4Addr >> 24 ) & 0xFF );
4567 }
4568
4569 // Set remaining parameters.
4570
4571 context->recordName = recordName;
4572 context->recordType = kDNSServiceType_PTR;
4573 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
4574 context->oneShotMode = gReverseLookup_OneShot ? true : false;
4575
4576 // Print prologue.
4577
4578 QueryRecordPrintPrologue( context );
4579
4580 // Start operation.
4581
4582 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4583 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
4584 kDNSServiceClass_IN, QueryRecordCallback, context );
4585 require_noerr( err, exit );
4586
4587 context->opRef = sdRef;
4588 if( !useMainConnection )
4589 {
4590 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4591 require_noerr( err, exit );
4592 }
4593
4594 // Set time limit.
4595
4596 if( context->timeLimitSecs > 0 )
4597 {
4598 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4599 kExitReason_TimeLimit, Exit );
4600 }
4601 dispatch_main();
4602
4603 exit:
4604 dispatch_source_forget( &signalSource );
4605 if( context ) QueryRecordContextFree( context );
4606 if( err ) exit( 1 );
4607 }
4608
4609 //===========================================================================================================================
4610 // PortMappingCmd
4611 //===========================================================================================================================
4612
4613 typedef struct
4614 {
4615 DNSServiceRef mainRef; // Main sdRef for shared connection.
4616 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
4617 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
4618 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4619 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4620 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
4621 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4622 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
4623 Boolean printedHeader; // True if results header was printed.
4624
4625 } PortMappingContext;
4626
4627 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
4628 static void PortMappingContextFree( PortMappingContext *inContext );
4629 static void DNSSD_API
4630 PortMappingCallback(
4631 DNSServiceRef inSDRef,
4632 DNSServiceFlags inFlags,
4633 uint32_t inInterfaceIndex,
4634 DNSServiceErrorType inError,
4635 uint32_t inExternalIPv4Address,
4636 DNSServiceProtocol inProtocol,
4637 uint16_t inInternalPort,
4638 uint16_t inExternalPort,
4639 uint32_t inTTL,
4640 void * inContext );
4641
4642 static void PortMappingCmd( void )
4643 {
4644 OSStatus err;
4645 PortMappingContext * context = NULL;
4646 DNSServiceRef sdRef;
4647 dispatch_source_t signalSource = NULL;
4648 int useMainConnection;
4649
4650 // Set up SIGINT handler.
4651
4652 signal( SIGINT, SIG_IGN );
4653 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4654 require_noerr( err, exit );
4655 dispatch_resume( signalSource );
4656
4657 // Create context.
4658
4659 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
4660 require_action( context, exit, err = kNoMemoryErr );
4661
4662 // Check command parameters.
4663
4664 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
4665 {
4666 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
4667 err = kParamErr;
4668 goto exit;
4669 }
4670
4671 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
4672 {
4673 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
4674 err = kParamErr;
4675 goto exit;
4676 }
4677
4678 // Create main connection.
4679
4680 if( gConnectionOpt )
4681 {
4682 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4683 require_noerr_quiet( err, exit );
4684 useMainConnection = true;
4685 }
4686 else
4687 {
4688 useMainConnection = false;
4689 }
4690
4691 // Get flags.
4692
4693 context->flags = GetDNSSDFlagsFromOpts();
4694 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4695
4696 // Get interface index.
4697
4698 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4699 require_noerr_quiet( err, exit );
4700
4701 // Set remaining parameters.
4702
4703 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
4704 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
4705 context->ttl = (uint32_t) gPortMapping_TTL;
4706 context->internalPort = (uint16_t) gPortMapping_InternalPort;
4707 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
4708
4709 // Print prologue.
4710
4711 PortMappingPrintPrologue( context );
4712
4713 // Start operation.
4714
4715 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4716 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
4717 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
4718 require_noerr( err, exit );
4719
4720 context->opRef = sdRef;
4721 if( !useMainConnection )
4722 {
4723 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4724 require_noerr( err, exit );
4725 }
4726
4727 dispatch_main();
4728
4729 exit:
4730 dispatch_source_forget( &signalSource );
4731 if( context ) PortMappingContextFree( context );
4732 if( err ) exit( 1 );
4733 }
4734
4735 //===========================================================================================================================
4736 // PortMappingPrintPrologue
4737 //===========================================================================================================================
4738
4739 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
4740 {
4741 char ifName[ kInterfaceNameBufLen ];
4742
4743 InterfaceIndexToName( inContext->ifIndex, ifName );
4744
4745 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4746 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4747 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
4748 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
4749 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
4750 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
4751 " (system will use a default value.)" );
4752 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4753 FPrintF( stdout, "---\n" );
4754
4755 }
4756
4757 //===========================================================================================================================
4758 // PortMappingContextFree
4759 //===========================================================================================================================
4760
4761 static void PortMappingContextFree( PortMappingContext *inContext )
4762 {
4763 DNSServiceForget( &inContext->opRef );
4764 DNSServiceForget( &inContext->mainRef );
4765 free( inContext );
4766 }
4767
4768 //===========================================================================================================================
4769 // PortMappingCallback
4770 //===========================================================================================================================
4771
4772 static void DNSSD_API
4773 PortMappingCallback(
4774 DNSServiceRef inSDRef,
4775 DNSServiceFlags inFlags,
4776 uint32_t inInterfaceIndex,
4777 DNSServiceErrorType inError,
4778 uint32_t inExternalIPv4Address,
4779 DNSServiceProtocol inProtocol,
4780 uint16_t inInternalPort,
4781 uint16_t inExternalPort,
4782 uint32_t inTTL,
4783 void * inContext )
4784 {
4785 PortMappingContext * const context = (PortMappingContext *) inContext;
4786 struct timeval now;
4787 char errorStr[ 128 ];
4788
4789 Unused( inSDRef );
4790 Unused( inFlags );
4791
4792 gettimeofday( &now, NULL );
4793
4794 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
4795 if( !context->printedHeader )
4796 {
4797 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4798 context->printedHeader = true;
4799 }
4800 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4801 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
4802 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
4803 }
4804
4805 //===========================================================================================================================
4806 // BrowseAllCmd
4807 //===========================================================================================================================
4808
4809 typedef struct BrowseAllConnection BrowseAllConnection;
4810
4811 typedef struct
4812 {
4813 ServiceBrowserRef browser; // Service browser.
4814 ServiceBrowserResults * results; // Results from the service browser.
4815 BrowseAllConnection * connectionList; // List of connections.
4816 dispatch_source_t connectionTimer; // Timer for connection timeout.
4817 int connectionPendingCount; // Number of pending connections.
4818 int connectionTimeoutSecs; // Timeout value for connections in seconds.
4819
4820 } BrowseAllContext;
4821
4822 struct BrowseAllConnection
4823 {
4824 BrowseAllConnection * next; // Next connection object in list.
4825 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
4826 uint16_t port; // TCP port to connect to.
4827 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
4828 OSStatus status; // Status of connection. NoErr means connection succeeded.
4829 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
4830 int32_t refCount; // This object's reference count.
4831 BrowseAllContext * context; // Back pointer to parent context.
4832 };
4833
4834 static void _BrowseAllContextFree( BrowseAllContext *inContext );
4835 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
4836 static OSStatus
4837 _BrowseAllConnectionCreate(
4838 const struct sockaddr * inSockAddr,
4839 uint16_t inPort,
4840 BrowseAllContext * inContext,
4841 BrowseAllConnection ** outConnection );
4842 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
4843 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
4844 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
4845 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
4846 static void _BrowseAllExit( void *inContext );
4847
4848 static Boolean _IsServiceTypeTCP( const char *inServiceType );
4849
4850 static void BrowseAllCmd( void )
4851 {
4852 OSStatus err;
4853 BrowseAllContext * context = NULL;
4854 size_t i;
4855 uint32_t ifIndex;
4856 char ifName[ kInterfaceNameBufLen ];
4857
4858 // Check parameters.
4859
4860 if( gBrowseAll_BrowseTimeSecs <= 0 )
4861 {
4862 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
4863 err = kParamErr;
4864 goto exit;
4865 }
4866
4867 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
4868 require_action( context, exit, err = kNoMemoryErr );
4869
4870 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
4871 #if( TARGET_OS_POSIX )
4872 // Increase the open file descriptor limit for connection sockets.
4873
4874 if( context->connectionTimeoutSecs > 0 )
4875 {
4876 struct rlimit fdLimits;
4877
4878 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
4879 err = map_global_noerr_errno( err );
4880 require_noerr( err, exit );
4881
4882 if( fdLimits.rlim_cur < 4096 )
4883 {
4884 fdLimits.rlim_cur = 4096;
4885 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
4886 err = map_global_noerr_errno( err );
4887 require_noerr( err, exit );
4888 }
4889 }
4890 #endif
4891
4892 // Get interface index.
4893
4894 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4895 require_noerr_quiet( err, exit );
4896
4897 // Print prologue.
4898
4899 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4900 FPrintF( stdout, "Service types: ");
4901 if( gBrowseAll_ServiceTypesCount > 0 )
4902 {
4903 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
4904 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
4905 {
4906 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
4907 }
4908 FPrintF( stdout, "\n" );
4909 }
4910 else
4911 {
4912 FPrintF( stdout, "all services\n" );
4913 }
4914 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
4915 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
4916 FPrintF( stdout, "Connect timeout: %d second%?c\n",
4917 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
4918 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
4919 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4920 FPrintF( stdout, "---\n" );
4921
4922 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
4923 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
4924 require_noerr( err, exit );
4925
4926 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
4927 {
4928 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
4929 require_noerr( err, exit );
4930 }
4931 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
4932 ServiceBrowserStart( context->browser );
4933 dispatch_main();
4934
4935 exit:
4936 if( context ) _BrowseAllContextFree( context );
4937 if( err ) exit( 1 );
4938 }
4939
4940 //===========================================================================================================================
4941 // _BrowseAllContextFree
4942 //===========================================================================================================================
4943
4944 static void _BrowseAllContextFree( BrowseAllContext *inContext )
4945 {
4946 check( !inContext->browser );
4947 check( !inContext->connectionTimer );
4948 check( !inContext->connectionList );
4949 ForgetServiceBrowserResults( &inContext->results );
4950 free( inContext );
4951 }
4952
4953 //===========================================================================================================================
4954 // _BrowseAllServiceBrowserCallback
4955 //===========================================================================================================================
4956
4957 #define kDiscardProtocolPort 9
4958
4959 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
4960 {
4961 OSStatus err;
4962 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4963 SBRDomain * domain;
4964 SBRServiceType * type;
4965 SBRServiceInstance * instance;
4966 SBRIPAddress * ipaddr;
4967
4968 Unused( inError );
4969
4970 require_action( inResults, exit, err = kUnexpectedErr );
4971
4972 check( !context->results );
4973 context->results = inResults;
4974 ServiceBrowserResultsRetain( context->results );
4975
4976 check( context->connectionPendingCount == 0 );
4977 if( context->connectionTimeoutSecs > 0 )
4978 {
4979 BrowseAllConnection * connection;
4980 BrowseAllConnection ** connectionPtr = &context->connectionList;
4981 char destination[ kSockAddrStringMaxSize ];
4982
4983 for( domain = context->results->domainList; domain; domain = domain->next )
4984 {
4985 for( type = domain->typeList; type; type = type->next )
4986 {
4987 if( !_IsServiceTypeTCP( type->name ) ) continue;
4988 for( instance = type->instanceList; instance; instance = instance->next )
4989 {
4990 if( instance->port == kDiscardProtocolPort ) continue;
4991 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
4992 {
4993 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
4994 require_noerr( err, exit );
4995
4996 *connectionPtr = connection;
4997 connectionPtr = &connection->next;
4998
4999 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
5000 check_noerr( err );
5001 if( !err )
5002 {
5003 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
5004 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
5005 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5006 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
5007 dispatch_get_main_queue() );
5008 check_noerr( err );
5009 }
5010 if( !err )
5011 {
5012 _BrowseAllConnectionRetain( connection );
5013 connection->status = kInProgressErr;
5014 ++context->connectionPendingCount;
5015 }
5016 else
5017 {
5018 connection->status = err;
5019 }
5020 }
5021 }
5022 }
5023 }
5024 }
5025
5026 if( context->connectionPendingCount > 0 )
5027 {
5028 check( !context->connectionTimer );
5029 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5030 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5031 require_noerr( err, exit );
5032 dispatch_resume( context->connectionTimer );
5033 }
5034 else
5035 {
5036 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5037 }
5038 err = kNoErr;
5039
5040 exit:
5041 ForgetCF( &context->browser );
5042 if( err ) exit( 1 );
5043 }
5044
5045 //===========================================================================================================================
5046 // _BrowseAllConnectionCreate
5047 //===========================================================================================================================
5048
5049 static OSStatus
5050 _BrowseAllConnectionCreate(
5051 const struct sockaddr * inSockAddr,
5052 uint16_t inPort,
5053 BrowseAllContext * inContext,
5054 BrowseAllConnection ** outConnection )
5055 {
5056 OSStatus err;
5057 BrowseAllConnection * obj;
5058
5059 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5060 require_action( obj, exit, err = kNoMemoryErr );
5061
5062 obj->refCount = 1;
5063 SockAddrCopy( inSockAddr, &obj->sip );
5064 obj->port = inPort;
5065 obj->context = inContext;
5066
5067 *outConnection = obj;
5068 err = kNoErr;
5069
5070 exit:
5071 return( err );
5072 }
5073
5074 //===========================================================================================================================
5075 // _BrowseAllConnectionRetain
5076 //===========================================================================================================================
5077
5078 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5079 {
5080 ++inConnection->refCount;
5081 }
5082
5083 //===========================================================================================================================
5084 // _BrowseAllConnectionRelease
5085 //===========================================================================================================================
5086
5087 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5088 {
5089 if( --inConnection->refCount == 0 ) free( inConnection );
5090 }
5091
5092 //===========================================================================================================================
5093 // _BrowseAllConnectionProgress
5094 //===========================================================================================================================
5095
5096 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5097 {
5098 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5099
5100 if( inPhase == kAsyncConnectionPhase_Connected )
5101 {
5102 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5103
5104 connection->connectTimeSecs = info->connectSecs;
5105 }
5106 }
5107
5108 //===========================================================================================================================
5109 // _BrowseAllConnectionHandler
5110 //===========================================================================================================================
5111
5112 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5113 {
5114 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5115 BrowseAllContext * const context = connection->context;
5116
5117 connection->status = inError;
5118 ForgetSocket( &inSock );
5119 if( context )
5120 {
5121 check( context->connectionPendingCount > 0 );
5122 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5123 {
5124 dispatch_source_forget( &context->connectionTimer );
5125 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5126 }
5127 }
5128 _BrowseAllConnectionRelease( connection );
5129 }
5130
5131 //===========================================================================================================================
5132 // _BrowseAllExit
5133 //===========================================================================================================================
5134
5135 #define Indent( X ) ( (X) * 4 ), ""
5136
5137 static void _BrowseAllExit( void *inContext )
5138 {
5139 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5140 SBRDomain * domain;
5141 SBRServiceType * type;
5142 SBRServiceInstance * instance;
5143 SBRIPAddress * ipaddr;
5144 char textBuf[ 512 ];
5145 #if( TARGET_OS_POSIX )
5146 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5147 #endif
5148
5149 dispatch_source_forget( &context->connectionTimer );
5150
5151 for( domain = context->results->domainList; domain; domain = domain->next )
5152 {
5153 FPrintF( stdout, "%s\n\n", domain->name );
5154
5155 for( type = domain->typeList; type; type = type->next )
5156 {
5157 const char * description;
5158 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5159
5160 description = ServiceTypeDescription( type->name );
5161 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5162 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5163
5164 for( instance = type->instanceList; instance; instance = instance->next )
5165 {
5166 char * dst = textBuf;
5167 char * const lim = &textBuf[ countof( textBuf ) ];
5168 char ifname[ IF_NAMESIZE + 1 ];
5169
5170 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5171 if( instance->ifIndex == 0 )
5172 {
5173 SNPrintF_Add( &dst, lim, "the Internet" );
5174 }
5175 else if( if_indextoname( instance->ifIndex, ifname ) )
5176 {
5177 NetTransportType netType;
5178
5179 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5180 SNPrintF_Add( &dst, lim, "%s (%s)",
5181 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5182 ifname );
5183 }
5184 else
5185 {
5186 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5187 }
5188 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5189 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5190
5191 if( instance->hostname )
5192 {
5193 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5194 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5195 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5196 }
5197 else
5198 {
5199 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5200 }
5201
5202 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5203 {
5204 BrowseAllConnection * conn;
5205 BrowseAllConnection ** connPtr;
5206
5207 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5208 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5209
5210 conn = NULL;
5211 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5212 {
5213 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5214 {
5215 if( ( conn->port == instance->port ) &&
5216 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5217 }
5218 if( conn )
5219 {
5220 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5221 *connPtr = conn->next;
5222 conn->context = NULL;
5223 AsyncConnection_Forget( &conn->asyncCnx );
5224 }
5225 }
5226
5227 if( conn )
5228 {
5229 if( conn->status == kNoErr )
5230 {
5231 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5232 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5233 }
5234 else
5235 {
5236 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5237 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5238 }
5239 _BrowseAllConnectionRelease( conn );
5240 }
5241 else
5242 {
5243 FPrintF( stdout, " (no connection attempted)\n" );
5244 }
5245 }
5246
5247 FPrintF( stdout, "\n" );
5248 if( instance->txtLen == 0 ) continue;
5249
5250 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5251 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5252 if( instance->txtLen > 1 )
5253 {
5254 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5255 }
5256 else
5257 {
5258 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5259 }
5260 FPrintF( stdout, "\n" );
5261 }
5262 FPrintF( stdout, "\n" );
5263 }
5264 }
5265
5266 _BrowseAllContextFree( context );
5267 Exit( NULL );
5268 }
5269
5270 //===========================================================================================================================
5271 // _IsServiceTypeTCP
5272 //===========================================================================================================================
5273
5274 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5275 {
5276 OSStatus err;
5277 const uint8_t * secondLabel;
5278 uint8_t name[ kDomainNameLengthMax ];
5279
5280 err = DomainNameFromString( name, inServiceType, NULL );
5281 if( !err )
5282 {
5283 secondLabel = DomainNameGetNextLabel( name );
5284 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5285 }
5286 return( false );
5287 }
5288
5289 //===========================================================================================================================
5290 // GetNameInfoCmd
5291 //===========================================================================================================================
5292
5293 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5294 {
5295 CaseFlagStringify( NI_NUMERICSCOPE ),
5296 CaseFlagStringify( NI_DGRAM ),
5297 CaseFlagStringify( NI_NUMERICSERV ),
5298 CaseFlagStringify( NI_NAMEREQD ),
5299 CaseFlagStringify( NI_NUMERICHOST ),
5300 CaseFlagStringify( NI_NOFQDN ),
5301 { 0, NULL }
5302 };
5303
5304 static void GetNameInfoCmd( void )
5305 {
5306 OSStatus err;
5307 sockaddr_ip sip;
5308 size_t sockAddrLen;
5309 unsigned int flags;
5310 const FlagStringPair * pair;
5311 struct timeval now;
5312 char host[ NI_MAXHOST ];
5313 char serv[ NI_MAXSERV ];
5314
5315 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5316 check_noerr( err );
5317 if( err )
5318 {
5319 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5320 goto exit;
5321 }
5322
5323 flags = 0;
5324 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5325 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5326 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5327 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5328 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5329 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5330
5331 // Print prologue.
5332
5333 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5334 FPrintF( stdout, "Flags: 0x%X < ", flags );
5335 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5336 {
5337 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5338 }
5339 FPrintF( stdout, ">\n" );
5340 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5341 FPrintF( stdout, "---\n" );
5342
5343 // Call getnameinfo().
5344
5345 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5346 (int) flags );
5347 gettimeofday( &now, NULL );
5348 if( err )
5349 {
5350 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5351 }
5352 else
5353 {
5354 FPrintF( stdout, "host: %s\n", host );
5355 FPrintF( stdout, "serv: %s\n", serv );
5356 }
5357 FPrintF( stdout, "---\n" );
5358 FPrintF( stdout, "End time: %{du:time}\n", &now );
5359
5360 exit:
5361 gExitCode = err ? 1 : 0;
5362 }
5363
5364 //===========================================================================================================================
5365 // GetAddrInfoStressCmd
5366 //===========================================================================================================================
5367
5368 typedef struct
5369 {
5370 DNSServiceRef mainRef;
5371 DNSServiceRef sdRef;
5372 DNSServiceFlags flags;
5373 unsigned int interfaceIndex;
5374 unsigned int connectionNumber;
5375 unsigned int requestCount;
5376 unsigned int requestCountMax;
5377 unsigned int requestCountLimit;
5378 unsigned int durationMinMs;
5379 unsigned int durationMaxMs;
5380
5381 } GAIStressContext;
5382
5383 static void GetAddrInfoStressEvent( void *inContext );
5384 static void DNSSD_API
5385 GetAddrInfoStressCallback(
5386 DNSServiceRef inSDRef,
5387 DNSServiceFlags inFlags,
5388 uint32_t inInterfaceIndex,
5389 DNSServiceErrorType inError,
5390 const char * inHostname,
5391 const struct sockaddr * inSockAddr,
5392 uint32_t inTTL,
5393 void * inContext );
5394
5395 static void GetAddrInfoStressCmd( void )
5396 {
5397 OSStatus err;
5398 GAIStressContext * context = NULL;
5399 int i;
5400 DNSServiceFlags flags;
5401 uint32_t ifIndex;
5402 char ifName[ kInterfaceNameBufLen ];
5403
5404 if( gGAIStress_TestDurationSecs < 0 )
5405 {
5406 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5407 err = kParamErr;
5408 goto exit;
5409 }
5410 if( gGAIStress_ConnectionCount <= 0 )
5411 {
5412 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5413 err = kParamErr;
5414 goto exit;
5415 }
5416 if( gGAIStress_DurationMinMs <= 0 )
5417 {
5418 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5419 err = kParamErr;
5420 goto exit;
5421 }
5422 if( gGAIStress_DurationMaxMs <= 0 )
5423 {
5424 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5425 err = kParamErr;
5426 goto exit;
5427 }
5428 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5429 {
5430 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5431 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5432 err = kParamErr;
5433 goto exit;
5434 }
5435 if( gGAIStress_RequestCountMax <= 0 )
5436 {
5437 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5438 err = kParamErr;
5439 goto exit;
5440 }
5441
5442 // Set flags.
5443
5444 flags = GetDNSSDFlagsFromOpts();
5445
5446 // Set interface index.
5447
5448 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5449 require_noerr_quiet( err, exit );
5450
5451 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5452 {
5453 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5454 require_action( context, exit, err = kNoMemoryErr );
5455
5456 context->flags = flags;
5457 context->interfaceIndex = ifIndex;
5458 context->connectionNumber = (unsigned int)( i + 1 );
5459 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5460 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5461 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5462
5463 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5464 context = NULL;
5465 }
5466
5467 if( gGAIStress_TestDurationSecs > 0 )
5468 {
5469 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5470 }
5471
5472 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5473 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5474 FPrintF( stdout, "Test duration: " );
5475 if( gGAIStress_TestDurationSecs == 0 )
5476 {
5477 FPrintF( stdout, "∞\n" );
5478 }
5479 else
5480 {
5481 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5482 }
5483 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5484 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5485 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5486 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5487 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5488 FPrintF( stdout, "---\n" );
5489
5490 dispatch_main();
5491
5492 exit:
5493 FreeNullSafe( context );
5494 if( err ) exit( 1 );
5495 }
5496
5497 //===========================================================================================================================
5498 // GetAddrInfoStressEvent
5499 //===========================================================================================================================
5500
5501 #define kStressRandStrLen 5
5502
5503 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5504
5505 static void GetAddrInfoStressEvent( void *inContext )
5506 {
5507 GAIStressContext * const context = (GAIStressContext *) inContext;
5508 OSStatus err;
5509 DNSServiceRef sdRef;
5510 unsigned int nextMs;
5511 char randomStr[ kStressRandStrLen + 1 ];
5512 char hostname[ kStressRandStrLen + 4 + 1 ];
5513 Boolean isConnectionNew = false;
5514 static Boolean printedHeader = false;
5515
5516 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5517 {
5518 DNSServiceForget( &context->mainRef );
5519 context->sdRef = NULL;
5520 context->requestCount = 0;
5521 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5522
5523 err = DNSServiceCreateConnection( &context->mainRef );
5524 require_noerr( err, exit );
5525
5526 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5527 require_noerr( err, exit );
5528
5529 isConnectionNew = true;
5530 }
5531
5532 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5533 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5534
5535 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5536
5537 if( !printedHeader )
5538 {
5539 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5540 printedHeader = true;
5541 }
5542 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5543 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5544
5545 DNSServiceForget( &context->sdRef );
5546 sdRef = context->mainRef;
5547 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5548 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5549 require_noerr( err, exit );
5550 context->sdRef = sdRef;
5551
5552 context->requestCount++;
5553
5554 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5555
5556 exit:
5557 if( err ) exit( 1 );
5558 }
5559
5560 //===========================================================================================================================
5561 // GetAddrInfoStressCallback
5562 //===========================================================================================================================
5563
5564 static void DNSSD_API
5565 GetAddrInfoStressCallback(
5566 DNSServiceRef inSDRef,
5567 DNSServiceFlags inFlags,
5568 uint32_t inInterfaceIndex,
5569 DNSServiceErrorType inError,
5570 const char * inHostname,
5571 const struct sockaddr * inSockAddr,
5572 uint32_t inTTL,
5573 void * inContext )
5574 {
5575 Unused( inSDRef );
5576 Unused( inFlags );
5577 Unused( inInterfaceIndex );
5578 Unused( inError );
5579 Unused( inHostname );
5580 Unused( inSockAddr );
5581 Unused( inTTL );
5582 Unused( inContext );
5583 }
5584
5585 //===========================================================================================================================
5586 // DNSQueryCmd
5587 //===========================================================================================================================
5588
5589 typedef struct
5590 {
5591 sockaddr_ip serverAddr;
5592 uint64_t sendTicks;
5593 uint8_t * msgPtr;
5594 size_t msgLen;
5595 size_t msgOffset;
5596 const char * name;
5597 dispatch_source_t readSource;
5598 SocketRef sock;
5599 int timeLimitSecs;
5600 uint16_t queryID;
5601 uint16_t type;
5602 Boolean haveTCPLen;
5603 Boolean useTCP;
5604 Boolean printRawRData; // True if RDATA results are not to be formatted.
5605 uint8_t msgBuf[ 512 ];
5606
5607 } DNSQueryContext;
5608
5609 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5610 static void DNSQueryReadHandler( void *inContext );
5611 static void DNSQueryCancelHandler( void *inContext );
5612
5613 static void DNSQueryCmd( void )
5614 {
5615 OSStatus err;
5616 DNSQueryContext * context = NULL;
5617 uint8_t * msgPtr;
5618 size_t msgLen, sendLen;
5619
5620 // Check command parameters.
5621
5622 if( gDNSQuery_TimeLimitSecs < -1 )
5623 {
5624 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5625 err = kParamErr;
5626 goto exit;
5627 }
5628 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5629 {
5630 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5631 err = kParamErr;
5632 goto exit;
5633 }
5634
5635 // Create context.
5636
5637 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5638 require_action( context, exit, err = kNoMemoryErr );
5639
5640 context->name = gDNSQuery_Name;
5641 context->sock = kInvalidSocketRef;
5642 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5643 context->queryID = (uint16_t) Random32();
5644 context->useTCP = gDNSQuery_UseTCP ? true : false;
5645 context->printRawRData = gDNSQuery_RawRData ? true : false;
5646
5647 #if( TARGET_OS_DARWIN )
5648 if( gDNSQuery_Server )
5649 #endif
5650 {
5651 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5652 require_noerr( err, exit );
5653 }
5654 #if( TARGET_OS_DARWIN )
5655 else
5656 {
5657 err = GetDefaultDNSServer( &context->serverAddr );
5658 require_noerr( err, exit );
5659 }
5660 #endif
5661 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5662
5663 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5664 require_noerr( err, exit );
5665
5666 // Write query message.
5667
5668 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5669
5670 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5671 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5672 kDNSServiceClass_IN, &msgLen );
5673 require_noerr( err, exit );
5674 check( msgLen <= UINT16_MAX );
5675
5676 if( context->useTCP )
5677 {
5678 WriteBig16( context->msgBuf, msgLen );
5679 sendLen = 2 + msgLen;
5680 }
5681 else
5682 {
5683 sendLen = msgLen;
5684 }
5685
5686 DNSQueryPrintPrologue( context );
5687
5688 if( gDNSQuery_Verbose )
5689 {
5690 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5691 FPrintF( stdout, "---\n" );
5692 }
5693
5694 if( context->useTCP )
5695 {
5696 // Create TCP socket.
5697
5698 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5699 err = map_socket_creation_errno( context->sock );
5700 require_noerr( err, exit );
5701
5702 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5703 require_noerr( err, exit );
5704 }
5705 else
5706 {
5707 // Create UDP socket.
5708
5709 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5710 require_noerr( err, exit );
5711 }
5712
5713 context->sendTicks = UpTicks();
5714 err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5715 require_noerr( err, exit );
5716
5717 if( context->timeLimitSecs == 0 ) goto exit;
5718
5719 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5720 &context->readSource );
5721 require_noerr( err, exit );
5722 dispatch_resume( context->readSource );
5723
5724 if( context->timeLimitSecs > 0 )
5725 {
5726 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5727 Exit );
5728 }
5729 dispatch_main();
5730
5731 exit:
5732 if( context )
5733 {
5734 dispatch_source_forget( &context->readSource );
5735 ForgetSocket( &context->sock );
5736 free( context );
5737 }
5738 if( err ) exit( 1 );
5739 }
5740
5741 //===========================================================================================================================
5742 // DNSQueryPrintPrologue
5743 //===========================================================================================================================
5744
5745 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5746 {
5747 const int timeLimitSecs = inContext->timeLimitSecs;
5748
5749 FPrintF( stdout, "Name: %s\n", inContext->name );
5750 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5751 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5752 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5753 FPrintF( stdout, "Time limit: " );
5754 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5755 else FPrintF( stdout, "∞\n" );
5756 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5757 FPrintF( stdout, "---\n" );
5758 }
5759
5760 //===========================================================================================================================
5761 // DNSQueryReadHandler
5762 //===========================================================================================================================
5763
5764 static void DNSQueryReadHandler( void *inContext )
5765 {
5766 OSStatus err;
5767 struct timeval now;
5768 const uint64_t nowTicks = UpTicks();
5769 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5770
5771 gettimeofday( &now, NULL );
5772
5773 if( context->useTCP )
5774 {
5775 if( !context->haveTCPLen )
5776 {
5777 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5778 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5779 require_noerr( err, exit );
5780
5781 context->msgOffset = 0;
5782 context->msgLen = ReadBig16( context->msgBuf );
5783 context->haveTCPLen = true;
5784 if( context->msgLen <= sizeof( context->msgBuf ) )
5785 {
5786 context->msgPtr = context->msgBuf;
5787 }
5788 else
5789 {
5790 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5791 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5792 }
5793 }
5794
5795 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5796 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5797 require_noerr( err, exit );
5798 context->msgOffset = 0;
5799 context->haveTCPLen = false;
5800 }
5801 else
5802 {
5803 sockaddr_ip fromAddr;
5804
5805 context->msgPtr = context->msgBuf;
5806 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5807 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5808 require_noerr( err, exit );
5809
5810 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5811 }
5812
5813 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
5814 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5815 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5816 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5817 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
5818
5819 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5820 {
5821 Exit( kExitReason_ReceivedResponse );
5822 }
5823
5824 exit:
5825 if( err ) dispatch_source_forget( &context->readSource );
5826 }
5827
5828 //===========================================================================================================================
5829 // DNSQueryCancelHandler
5830 //===========================================================================================================================
5831
5832 static void DNSQueryCancelHandler( void *inContext )
5833 {
5834 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5835
5836 check( !context->readSource );
5837 ForgetSocket( &context->sock );
5838 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5839 free( context );
5840 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5841 }
5842
5843 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5844 //===========================================================================================================================
5845 // DNSCryptCmd
5846 //===========================================================================================================================
5847
5848 #define kDNSCryptPort 443
5849
5850 #define kDNSCryptMinPadLength 8
5851 #define kDNSCryptMaxPadLength 256
5852 #define kDNSCryptBlockSize 64
5853 #define kDNSCryptCertMinimumLength 124
5854 #define kDNSCryptClientMagicLength 8
5855 #define kDNSCryptResolverMagicLength 8
5856 #define kDNSCryptHalfNonceLength 12
5857 #define kDNSCryptCertMagicLength 4
5858
5859 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5860
5861 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5862 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5863 {
5864 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5865 };
5866
5867 typedef struct
5868 {
5869 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5870 uint8_t esVersion[ 2 ];
5871 uint8_t minorVersion[ 2 ];
5872 uint8_t signature[ crypto_sign_BYTES ];
5873 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5874 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5875 uint8_t serial[ 4 ];
5876 uint8_t startTime[ 4 ];
5877 uint8_t endTime[ 4 ];
5878 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5879
5880 } DNSCryptCert;
5881
5882 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5883
5884 typedef struct
5885 {
5886 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5887 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5888 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5889 uint8_t poly1305MAC[ 16 ];
5890
5891 } DNSCryptQueryHeader;
5892
5893 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5894 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5895 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5896 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5897
5898 typedef struct
5899 {
5900 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5901 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5902 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5903 uint8_t poly1305MAC[ 16 ];
5904
5905 } DNSCryptResponseHeader;
5906
5907 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5908 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5909 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5910 sizeof( DNSCryptResponseHeader ) );
5911
5912 typedef struct
5913 {
5914 sockaddr_ip serverAddr;
5915 uint64_t sendTicks;
5916 const char * providerName;
5917 const char * qname;
5918 const uint8_t * certPtr;
5919 size_t certLen;
5920 dispatch_source_t readSource;
5921 size_t msgLen;
5922 int timeLimitSecs;
5923 uint16_t queryID;
5924 uint16_t qtype;
5925 Boolean printRawRData;
5926 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5927 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5928 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5929 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5930 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5931 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5932 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5933 uint8_t msgBuf[ 512 ];
5934
5935 } DNSCryptContext;
5936
5937 static void DNSCryptReceiveCertHandler( void *inContext );
5938 static void DNSCryptReceiveResponseHandler( void *inContext );
5939 static void DNSCryptProceed( void *inContext );
5940 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5941 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5942 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5943 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5944
5945 static void DNSCryptCmd( void )
5946 {
5947 OSStatus err;
5948 DNSCryptContext * context = NULL;
5949 size_t writtenBytes;
5950 size_t totalBytes;
5951 SocketContext * sockCtx;
5952 SocketRef sock = kInvalidSocketRef;
5953 const char * ptr;
5954
5955 // Check command parameters.
5956
5957 if( gDNSCrypt_TimeLimitSecs < -1 )
5958 {
5959 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5960 err = kParamErr;
5961 goto exit;
5962 }
5963
5964 // Create context.
5965
5966 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5967 require_action( context, exit, err = kNoMemoryErr );
5968
5969 context->providerName = gDNSCrypt_ProviderName;
5970 context->qname = gDNSCrypt_Name;
5971 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5972 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5973
5974 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5975 require_noerr( err, exit );
5976
5977 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5978 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5979 if( err || ( *ptr != '\0' ) )
5980 {
5981 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5982 goto exit;
5983 }
5984 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5985 {
5986 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5987 totalBytes, sizeof( context->serverPublicSignKey ) );
5988 err = kSizeErr;
5989 goto exit;
5990 }
5991 check( writtenBytes == totalBytes );
5992
5993 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5994 require_noerr( err, exit );
5995 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
5996
5997 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
5998 require_noerr( err, exit );
5999
6000 // Write query message.
6001
6002 context->queryID = (uint16_t) Random32();
6003 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6004 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6005 require_noerr( err, exit );
6006
6007 // Create UDP socket.
6008
6009 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6010 require_noerr( err, exit );
6011
6012 // Send DNS query.
6013
6014 context->sendTicks = UpTicks();
6015 err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6016 require_noerr( err, exit );
6017
6018 err = SocketContextCreate( sock, context, &sockCtx );
6019 require_noerr( err, exit );
6020 sock = kInvalidSocketRef;
6021
6022 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6023 &context->readSource );
6024 if( err ) ForgetSocketContext( &sockCtx );
6025 require_noerr( err, exit );
6026
6027 dispatch_resume( context->readSource );
6028
6029 if( context->timeLimitSecs > 0 )
6030 {
6031 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6032 Exit );
6033 }
6034 dispatch_main();
6035
6036 exit:
6037 if( context ) free( context );
6038 ForgetSocket( &sock );
6039 if( err ) exit( 1 );
6040 }
6041
6042 //===========================================================================================================================
6043 // DNSCryptReceiveCertHandler
6044 //===========================================================================================================================
6045
6046 static void DNSCryptReceiveCertHandler( void *inContext )
6047 {
6048 OSStatus err;
6049 struct timeval now;
6050 const uint64_t nowTicks = UpTicks();
6051 SocketContext * const sockCtx = (SocketContext *) inContext;
6052 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6053 const DNSHeader * hdr;
6054 sockaddr_ip fromAddr;
6055 const uint8_t * ptr;
6056 const uint8_t * txtPtr;
6057 size_t txtLen;
6058 unsigned int answerCount, i;
6059 uint8_t targetName[ kDomainNameLengthMax ];
6060
6061 gettimeofday( &now, NULL );
6062
6063 dispatch_source_forget( &context->readSource );
6064
6065 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6066 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6067 require_noerr( err, exit );
6068 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6069
6070 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6071 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6072 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6073 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6074 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6075
6076 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6077
6078 hdr = (DNSHeader *) context->msgBuf;
6079 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6080
6081 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6082 require_noerr( err, exit );
6083
6084 err = DomainNameFromString( targetName, context->providerName, NULL );
6085 require_noerr( err, exit );
6086
6087 answerCount = DNSHeaderGetAnswerCount( hdr );
6088 for( i = 0; i < answerCount; ++i )
6089 {
6090 uint16_t type;
6091 uint16_t class;
6092 uint8_t name[ kDomainNameLengthMax ];
6093
6094 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6095 &ptr );
6096 require_noerr( err, exit );
6097
6098 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6099 {
6100 break;
6101 }
6102 }
6103
6104 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6105 {
6106 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6107 err = kSizeErr;
6108 goto exit;
6109 }
6110 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6111 {
6112 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6113 err = kSizeErr;
6114 goto exit;
6115 }
6116
6117 context->certLen = txtPtr[ 0 ];
6118 context->certPtr = &txtPtr[ 1 ];
6119
6120 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6121
6122 exit:
6123 if( err ) Exit( NULL );
6124 }
6125
6126 //===========================================================================================================================
6127 // DNSCryptReceiveResponseHandler
6128 //===========================================================================================================================
6129
6130 static void DNSCryptReceiveResponseHandler( void *inContext )
6131 {
6132 OSStatus err;
6133 struct timeval now;
6134 const uint64_t nowTicks = UpTicks();
6135 SocketContext * const sockCtx = (SocketContext *) inContext;
6136 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6137 sockaddr_ip fromAddr;
6138 DNSCryptResponseHeader * hdr;
6139 const uint8_t * end;
6140 uint8_t * ciphertext;
6141 uint8_t * plaintext;
6142 const uint8_t * response;
6143 uint8_t nonce[ crypto_box_NONCEBYTES ];
6144
6145 gettimeofday( &now, NULL );
6146
6147 dispatch_source_forget( &context->readSource );
6148
6149 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6150 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6151 require_noerr( err, exit );
6152 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6153
6154 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6155 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6156 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6157 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6158
6159 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6160 {
6161 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6162 err = kSizeErr;
6163 goto exit;
6164 }
6165
6166 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6167
6168 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6169 {
6170 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6171 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6172 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6173 err = kValueErr;
6174 goto exit;
6175 }
6176
6177 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6178 {
6179 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6180 err = kValueErr;
6181 goto exit;
6182 }
6183
6184 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6185
6186 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6187 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6188
6189 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6190 check( plaintext == ciphertext );
6191
6192 end = context->msgBuf + context->msgLen;
6193
6194 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6195 require_noerr( err, exit );
6196
6197 response = plaintext + crypto_box_ZEROBYTES;
6198 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6199 Exit( kExitReason_ReceivedResponse );
6200
6201 exit:
6202 if( err ) Exit( NULL );
6203 }
6204
6205 //===========================================================================================================================
6206 // DNSCryptProceed
6207 //===========================================================================================================================
6208
6209 static void DNSCryptProceed( void *inContext )
6210 {
6211 OSStatus err;
6212 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6213
6214 err = DNSCryptProcessCert( context );
6215 require_noerr_quiet( err, exit );
6216
6217 err = DNSCryptBuildQuery( context );
6218 require_noerr_quiet( err, exit );
6219
6220 err = DNSCryptSendQuery( context );
6221 require_noerr_quiet( err, exit );
6222
6223 exit:
6224 if( err ) Exit( NULL );
6225 }
6226
6227 //===========================================================================================================================
6228 // DNSCryptProcessCert
6229 //===========================================================================================================================
6230
6231 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6232 {
6233 OSStatus err;
6234 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6235 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6236 struct timeval now;
6237 time_t startTimeSecs, endTimeSecs;
6238 size_t signedLen;
6239 uint8_t * tempBuf;
6240 unsigned long long tempLen;
6241
6242 DNSCryptPrintCertificate( cert, inContext->certLen );
6243
6244 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6245 {
6246 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6247 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6248 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6249 err = kValueErr;
6250 goto exit;
6251 }
6252
6253 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6254 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6255
6256 gettimeofday( &now, NULL );
6257 if( now.tv_sec < startTimeSecs )
6258 {
6259 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6260 err = kDateErr;
6261 goto exit;
6262 }
6263 if( now.tv_sec >= endTimeSecs )
6264 {
6265 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6266 err = kDateErr;
6267 goto exit;
6268 }
6269
6270 signedLen = (size_t)( certEnd - cert->signature );
6271 tempBuf = (uint8_t *) malloc( signedLen );
6272 require_action( tempBuf, exit, err = kNoMemoryErr );
6273 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6274 free( tempBuf );
6275 if( err )
6276 {
6277 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6278 err = kAuthenticationErr;
6279 goto exit;
6280 }
6281
6282 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6283 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6284
6285 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6286 require_noerr( err, exit );
6287
6288 inContext->certPtr = NULL;
6289 inContext->certLen = 0;
6290 inContext->msgLen = 0;
6291
6292 exit:
6293 return( err );
6294 }
6295
6296 //===========================================================================================================================
6297 // DNSCryptBuildQuery
6298 //===========================================================================================================================
6299
6300 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6301
6302 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6303 {
6304 OSStatus err;
6305 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6306 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6307 size_t queryLen;
6308 size_t paddedQueryLen;
6309 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6310 const uint8_t * padLimit;
6311 uint8_t nonce[ crypto_box_NONCEBYTES ];
6312
6313 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6314
6315 inContext->queryID = (uint16_t) Random32();
6316 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6317 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6318 require_noerr( err, exit );
6319
6320 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6321 if( padLimit > msgLimit ) padLimit = msgLimit;
6322
6323 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6324 require_noerr( err, exit );
6325
6326 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6327 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6328 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6329 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6330
6331 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6332 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6333 require_noerr( err, exit );
6334
6335 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6336 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6337 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6338
6339 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6340
6341 exit:
6342 return( err );
6343 }
6344
6345 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6346 {
6347 OSStatus err;
6348 size_t paddedLen;
6349
6350 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6351
6352 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6353 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6354 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6355 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6356
6357 inMsgPtr[ inMsgLen ] = 0x80;
6358 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6359
6360 if( outPaddedLen ) *outPaddedLen = paddedLen;
6361 err = kNoErr;
6362
6363 exit:
6364 return( err );
6365 }
6366
6367 //===========================================================================================================================
6368 // DNSCryptSendQuery
6369 //===========================================================================================================================
6370
6371 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6372 {
6373 OSStatus err;
6374 SocketContext * sockCtx;
6375 SocketRef sock = kInvalidSocketRef;
6376
6377 check( inContext->msgLen > 0 );
6378 check( !inContext->readSource );
6379
6380 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6381 require_noerr( err, exit );
6382
6383 inContext->sendTicks = UpTicks();
6384 err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6385 require_noerr( err, exit );
6386
6387 err = SocketContextCreate( sock, inContext, &sockCtx );
6388 require_noerr( err, exit );
6389 sock = kInvalidSocketRef;
6390
6391 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6392 &inContext->readSource );
6393 if( err ) ForgetSocketContext( &sockCtx );
6394 require_noerr( err, exit );
6395
6396 dispatch_resume( inContext->readSource );
6397
6398 exit:
6399 ForgetSocket( &sock );
6400 return( err );
6401 }
6402
6403 //===========================================================================================================================
6404 // DNSCryptPrintCertificate
6405 //===========================================================================================================================
6406
6407 #define kCertTimeStrBufLen 32
6408
6409 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6410
6411 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6412 {
6413 time_t startTime, endTime;
6414 int extLen;
6415 char timeBuf[ kCertTimeStrBufLen ];
6416
6417 check( inLen >= kDNSCryptCertMinimumLength );
6418
6419 startTime = (time_t) ReadBig32( inCert->startTime );
6420 endTime = (time_t) ReadBig32( inCert->endTime );
6421
6422 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6423 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6424 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6425 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6426 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6427 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6428 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6429 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6430 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6431 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6432 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6433
6434 if( inLen > kDNSCryptCertMinimumLength )
6435 {
6436 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6437 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6438 }
6439 FPrintF( stdout, "\n" );
6440 }
6441
6442 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6443 {
6444 struct tm * tm;
6445
6446 tm = localtime( &inTime );
6447 if( !tm )
6448 {
6449 dlogassert( "localtime() returned a NULL pointer.\n" );
6450 *inBuffer = '\0';
6451 }
6452 else
6453 {
6454 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6455 }
6456
6457 return( inBuffer );
6458 }
6459
6460 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6461
6462 //===========================================================================================================================
6463 // MDNSQueryCmd
6464 //===========================================================================================================================
6465
6466 typedef struct
6467 {
6468 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6469 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6470 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6471 int localPort; // The port number to which the sockets are bound.
6472 int receiveSecs; // After send, the amount of time to spend receiving.
6473 uint32_t ifIndex; // Index of the interface over which to send the query.
6474 uint16_t qtype; // The type (QTYPE) of the record being queried.
6475 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6476 Boolean allResponses; // True if all mDNS messages received should be printed.
6477 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6478 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6479 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6480 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6481 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6482 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
6483
6484 } MDNSQueryContext;
6485
6486 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6487 static void MDNSQueryReadHandler( void *inContext );
6488
6489 static void MDNSQueryCmd( void )
6490 {
6491 OSStatus err;
6492 MDNSQueryContext * context;
6493 SocketRef sockV4 = kInvalidSocketRef;
6494 SocketRef sockV6 = kInvalidSocketRef;
6495 ssize_t n;
6496 const char * ifname;
6497 size_t msgLen;
6498 unsigned int sendCount;
6499
6500 // Check command parameters.
6501
6502 if( gMDNSQuery_ReceiveSecs < -1 )
6503 {
6504 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6505 err = kParamErr;
6506 goto exit;
6507 }
6508
6509 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6510 require_action( context, exit, err = kNoMemoryErr );
6511
6512 context->qnameStr = gMDNSQuery_Name;
6513 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6514 context->isQU = gMDNSQuery_IsQU ? true : false;
6515 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6516 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6517 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6518 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6519
6520 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6521 require_noerr_quiet( err, exit );
6522
6523 ifname = if_indextoname( context->ifIndex, context->ifName );
6524 require_action( ifname, exit, err = kNameErr );
6525
6526 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6527 require_noerr( err, exit );
6528
6529 // Set up IPv4 socket.
6530
6531 if( context->useIPv4 )
6532 {
6533 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6534 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6535 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
6536 require_noerr( err, exit );
6537 }
6538
6539 // Set up IPv6 socket.
6540
6541 if( context->useIPv6 )
6542 {
6543 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6544 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6545 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
6546 require_noerr( err, exit );
6547 }
6548
6549 // Craft mDNS query message.
6550
6551 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6552 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6553 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6554 require_noerr( err, exit );
6555
6556 // Print prologue.
6557
6558 MDNSQueryPrintPrologue( context );
6559
6560 // Send mDNS query message.
6561
6562 sendCount = 0;
6563 if( IsValidSocket( sockV4 ) )
6564 {
6565 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
6566
6567 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
6568 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6569 if( err )
6570 {
6571 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6572 ForgetSocket( &sockV4 );
6573 }
6574 else
6575 {
6576 ++sendCount;
6577 }
6578 }
6579 if( IsValidSocket( sockV6 ) )
6580 {
6581 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
6582
6583 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
6584 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6585 if( err )
6586 {
6587 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6588 ForgetSocket( &sockV6 );
6589 }
6590 else
6591 {
6592 ++sendCount;
6593 }
6594 }
6595 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6596
6597 // If there's no wait period after the send, then exit.
6598
6599 if( context->receiveSecs == 0 ) goto exit;
6600
6601 // Create dispatch read sources for socket(s).
6602
6603 if( IsValidSocket( sockV4 ) )
6604 {
6605 SocketContext * sockCtx;
6606
6607 err = SocketContextCreate( sockV4, context, &sockCtx );
6608 require_noerr( err, exit );
6609 sockV4 = kInvalidSocketRef;
6610
6611 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6612 &context->readSourceV4 );
6613 if( err ) ForgetSocketContext( &sockCtx );
6614 require_noerr( err, exit );
6615
6616 dispatch_resume( context->readSourceV4 );
6617 }
6618
6619 if( IsValidSocket( sockV6 ) )
6620 {
6621 SocketContext * sockCtx;
6622
6623 err = SocketContextCreate( sockV6, context, &sockCtx );
6624 require_noerr( err, exit );
6625 sockV6 = kInvalidSocketRef;
6626
6627 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6628 &context->readSourceV6 );
6629 if( err ) ForgetSocketContext( &sockCtx );
6630 require_noerr( err, exit );
6631
6632 dispatch_resume( context->readSourceV6 );
6633 }
6634
6635 if( context->receiveSecs > 0 )
6636 {
6637 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6638 Exit );
6639 }
6640 dispatch_main();
6641
6642 exit:
6643 ForgetSocket( &sockV4 );
6644 ForgetSocket( &sockV6 );
6645 if( err ) exit( 1 );
6646 }
6647
6648 //===========================================================================================================================
6649 // MDNSColliderCmd
6650 //===========================================================================================================================
6651
6652 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
6653
6654 static void MDNSColliderCmd( void )
6655 {
6656 OSStatus err;
6657 MDNSColliderRef collider = NULL;
6658 uint8_t * rdataPtr = NULL;
6659 size_t rdataLen = 0;
6660 const char * ifname;
6661 uint32_t ifIndex;
6662 MDNSColliderProtocols protocols;
6663 uint16_t type;
6664 char ifName[ IF_NAMESIZE + 1 ];
6665 uint8_t name[ kDomainNameLengthMax ];
6666
6667 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6668 require_noerr_quiet( err, exit );
6669
6670 ifname = if_indextoname( ifIndex, ifName );
6671 if( !ifname )
6672 {
6673 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
6674 err = kNameErr;
6675 goto exit;
6676 }
6677
6678 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
6679 if( err )
6680 {
6681 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
6682 goto exit;
6683 }
6684
6685 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
6686 require_noerr_quiet( err, exit );
6687
6688 if( gMDNSCollider_RecordData )
6689 {
6690 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
6691 require_noerr_quiet( err, exit );
6692 }
6693
6694 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
6695 require_noerr( err, exit );
6696
6697 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
6698 if( err )
6699 {
6700 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
6701 goto exit;
6702 }
6703
6704 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
6705 require_noerr( err, exit );
6706 ForgetMem( &rdataPtr );
6707
6708 protocols = kMDNSColliderProtocol_None;
6709 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
6710 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
6711 MDNSColliderSetProtocols( collider, protocols );
6712 MDNSColliderSetInterfaceIndex( collider, ifIndex );
6713 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
6714
6715 err = MDNSColliderStart( collider );
6716 require_noerr( err, exit );
6717
6718 dispatch_main();
6719
6720 exit:
6721 FreeNullSafe( rdataPtr );
6722 CFReleaseNullSafe( collider );
6723 if( err ) exit( 1 );
6724 }
6725
6726 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
6727 {
6728 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
6729
6730 CFRelease( collider );
6731 exit( inError ? 1 : 0 );
6732 }
6733
6734 //===========================================================================================================================
6735 // MDNSQueryPrintPrologue
6736 //===========================================================================================================================
6737
6738 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6739 {
6740 const int receiveSecs = inContext->receiveSecs;
6741
6742 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6743 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6744 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6745 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6746 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6747 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6748 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6749 FPrintF( stdout, "Receive duration: " );
6750 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6751 else FPrintF( stdout, "∞\n" );
6752 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6753 }
6754
6755 //===========================================================================================================================
6756 // MDNSQueryReadHandler
6757 //===========================================================================================================================
6758
6759 static void MDNSQueryReadHandler( void *inContext )
6760 {
6761 OSStatus err;
6762 struct timeval now;
6763 SocketContext * const sockCtx = (SocketContext *) inContext;
6764 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
6765 size_t msgLen;
6766 sockaddr_ip fromAddr;
6767 Boolean foundAnswer = false;
6768
6769 gettimeofday( &now, NULL );
6770
6771 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6772 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6773 require_noerr( err, exit );
6774
6775 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6776 {
6777 const uint8_t * ptr;
6778 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6779 unsigned int rrCount, i;
6780 uint16_t type, class;
6781 uint8_t name[ kDomainNameLengthMax ];
6782
6783 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6784 require_noerr( err, exit );
6785
6786 if( context->qname[ 0 ] == 0 )
6787 {
6788 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6789 require_noerr( err, exit );
6790 }
6791
6792 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6793 for( i = 0; i < rrCount; ++i )
6794 {
6795 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6796 require_noerr( err, exit );
6797
6798 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6799 DomainNameEqual( name, context->qname ) )
6800 {
6801 foundAnswer = true;
6802 break;
6803 }
6804 }
6805 }
6806 if( context->allResponses || foundAnswer )
6807 {
6808 FPrintF( stdout, "---\n" );
6809 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6810 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6811 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6812 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
6813 }
6814
6815 exit:
6816 if( err ) exit( 1 );
6817 }
6818
6819 //===========================================================================================================================
6820 // PIDToUUIDCmd
6821 //===========================================================================================================================
6822
6823 static void PIDToUUIDCmd( void )
6824 {
6825 OSStatus err;
6826 int n;
6827 struct proc_uniqidentifierinfo info;
6828
6829 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6830 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6831
6832 FPrintF( stdout, "%#U\n", info.p_uuid );
6833 err = kNoErr;
6834
6835 exit:
6836 if( err ) exit( 1 );
6837 }
6838
6839 //===========================================================================================================================
6840 // DNSServerCmd
6841 //===========================================================================================================================
6842
6843 typedef struct DNSServerPrivate * DNSServerRef;
6844
6845 typedef struct
6846 {
6847 DNSServerRef server; // Reference to the DNS server.
6848 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
6849 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
6850 const char * domainOverride; // If non-NULL, the server is to use this domain instead of "d.test.".
6851 #if( TARGET_OS_DARWIN )
6852 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
6853 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
6854 Boolean addedResolver; // True if system DNS settings contains a resolver entry for server.
6855 #endif
6856 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
6857
6858 } DNSServerCmdContext;
6859
6860 typedef enum
6861 {
6862 kDNSServerEvent_Started = 1,
6863 kDNSServerEvent_Stopped = 2
6864
6865 } DNSServerEventType;
6866
6867 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6868
6869 CFTypeID DNSServerGetTypeID( void );
6870 static OSStatus
6871 DNSServerCreate(
6872 dispatch_queue_t inQueue,
6873 DNSServerEventHandler_f inEventHandler,
6874 void * inEventContext,
6875 unsigned int inResponseDelayMs,
6876 uint32_t inDefaultTTL,
6877 int inPort,
6878 Boolean inLoopbackOnly,
6879 const char * inDomain,
6880 Boolean inBadUDPMode,
6881 DNSServerRef * outServer );
6882 static void DNSServerStart( DNSServerRef inServer );
6883 static void DNSServerStop( DNSServerRef inServer );
6884
6885 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6886
6887 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
6888 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6889 static void DNSServerCmdSigIntHandler( void *inContext );
6890 static void DNSServerCmdSigTermHandler( void *inContext );
6891 #if( TARGET_OS_DARWIN )
6892 static void DNSServerCmdFollowedProcessHandler( void *inContext );
6893 #endif
6894
6895 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
6896 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6897
6898 static void DNSServerCmd( void )
6899 {
6900 OSStatus err;
6901 DNSServerCmdContext * context = NULL;
6902
6903 if( gDNSServer_Foreground )
6904 {
6905 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6906 }
6907
6908 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
6909 require_noerr_quiet( err, exit );
6910
6911 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
6912 require_noerr_quiet( err, exit );
6913
6914 err = CheckIntegerArgument( gDNSServer_Port, "port number", -UINT16_MAX, UINT16_MAX );
6915 require_noerr_quiet( err, exit );
6916
6917 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
6918 require_action( context, exit, err = kNoMemoryErr );
6919
6920 context->domainOverride = gDNSServer_DomainOverride;
6921 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
6922
6923 #if( TARGET_OS_DARWIN )
6924 if( gDNSServer_FollowPID )
6925 {
6926 context->followPID = _StringToPID( gDNSServer_FollowPID, &err );
6927 if( err || ( context->followPID < 0 ) )
6928 {
6929 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
6930 err = kParamErr;
6931 goto exit;
6932 }
6933
6934 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
6935 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
6936 require_noerr( err, exit );
6937 dispatch_resume( context->processMonitor );
6938 }
6939 else
6940 {
6941 context->followPID = -1;
6942 }
6943 #endif
6944
6945 signal( SIGINT, SIG_IGN );
6946 err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
6947 require_noerr( err, exit );
6948 dispatch_resume( context->sigIntSource );
6949
6950 signal( SIGTERM, SIG_IGN );
6951 err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
6952 require_noerr( err, exit );
6953 dispatch_resume( context->sigTermSource );
6954
6955 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context,
6956 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, gDNSServer_Port, context->loopbackOnly,
6957 context->domainOverride, gDNSServer_BadUDPMode ? true : false, &context->server );
6958 require_noerr( err, exit );
6959
6960 DNSServerStart( context->server );
6961 dispatch_main();
6962
6963 exit:
6964 FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
6965 if( context ) DNSServerCmdContextFree( context );
6966 if( err ) exit( 1 );
6967 }
6968
6969 //===========================================================================================================================
6970 // DNSServerCmdContextFree
6971 //===========================================================================================================================
6972
6973 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
6974 {
6975 ForgetCF( &inContext->server );
6976 dispatch_source_forget( &inContext->sigIntSource );
6977 dispatch_source_forget( &inContext->sigTermSource );
6978 #if( TARGET_OS_DARWIN )
6979 dispatch_source_forget( &inContext->processMonitor );
6980 #endif
6981 free( inContext );
6982 }
6983
6984 //===========================================================================================================================
6985 // DNSServerCmdEventHandler
6986 //===========================================================================================================================
6987
6988 #if( TARGET_OS_DARWIN )
6989 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort );
6990 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
6991 #endif
6992
6993 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext )
6994 {
6995 OSStatus err;
6996 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
6997
6998 if( inType == kDNSServerEvent_Started )
6999 {
7000 #if( TARGET_OS_DARWIN )
7001 const int port = (int) inEventData;
7002
7003 err = _DNSServerCmdLoopbackResolverAdd( context->domainOverride ? context->domainOverride : "d.test.", port );
7004 if( err )
7005 {
7006 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7007 err );
7008 if( context->loopbackOnly ) ForgetDNSServer( &context->server );
7009 }
7010 else
7011 {
7012 context->addedResolver = true;
7013 }
7014 #endif
7015 }
7016 else if( inType == kDNSServerEvent_Stopped )
7017 {
7018 const OSStatus stopError = (OSStatus) inEventData;
7019
7020 if( stopError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", stopError );
7021
7022 err = kNoErr;
7023 #if( TARGET_OS_DARWIN )
7024 if( context->addedResolver )
7025 {
7026 err = _DNSServerCmdLoopbackResolverRemove();
7027 if( err )
7028 {
7029 ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7030 }
7031 else
7032 {
7033 context->addedResolver = false;
7034 }
7035 }
7036 else if( context->loopbackOnly )
7037 {
7038 err = kUnknownErr;
7039 }
7040 #endif
7041 DNSServerCmdContextFree( context );
7042 exit( ( stopError || err ) ? 1 : 0 );
7043 }
7044 }
7045
7046 #if( TARGET_OS_DARWIN )
7047 //===========================================================================================================================
7048 // _DNSServerCmdLoopbackResolverAdd
7049 //===========================================================================================================================
7050
7051 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort )
7052 {
7053 OSStatus err;
7054 SCDynamicStoreRef store;
7055 CFPropertyListRef plist = NULL;
7056 CFStringRef key = NULL;
7057 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7058 Boolean success;
7059
7060 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7061 err = map_scerror( store );
7062 require_noerr( err, exit );
7063
7064 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7065 "{"
7066 "%kO="
7067 "["
7068 "%s"
7069 "]"
7070 "%kO="
7071 "["
7072 "%.4a"
7073 "%.16a"
7074 "]"
7075 "%kO=%i"
7076 "%kO=%O"
7077 "%kO=%O"
7078 "}",
7079 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7080 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr,
7081 kSCPropNetDNSServerPort, inPort,
7082 kSCPropInterfaceName, CFSTR( "lo0" ),
7083 kSCPropNetDNSConfirmedServiceID, CFSTR( "com.apple.dnssdutil.server" ) );
7084 require_noerr( err, exit );
7085
7086 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7087 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7088 require_action( key, exit, err = kUnknownErr );
7089
7090 success = SCDynamicStoreSetValue( store, key, plist );
7091 require_action( success, exit, err = kUnknownErr );
7092
7093 exit:
7094 CFReleaseNullSafe( store );
7095 CFReleaseNullSafe( plist );
7096 CFReleaseNullSafe( key );
7097 return( err );
7098 }
7099
7100 //===========================================================================================================================
7101 // _DNSServerCmdLoopbackResolverRemove
7102 //===========================================================================================================================
7103
7104 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7105 {
7106 OSStatus err;
7107 SCDynamicStoreRef store;
7108 CFStringRef key = NULL;
7109 Boolean success;
7110
7111 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7112 err = map_scerror( store );
7113 require_noerr( err, exit );
7114
7115 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7116 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7117 require_action( key, exit, err = kUnknownErr );
7118
7119 success = SCDynamicStoreRemoveValue( store, key );
7120 require_action( success, exit, err = kUnknownErr );
7121
7122 exit:
7123 CFReleaseNullSafe( store );
7124 CFReleaseNullSafe( key );
7125 return( err );
7126 }
7127 #endif
7128
7129 //===========================================================================================================================
7130 // DNSServerCmdSigIntHandler
7131 //===========================================================================================================================
7132
7133 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal );
7134
7135 static void DNSServerCmdSigIntHandler( void *inContext )
7136 {
7137 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGINT );
7138 }
7139
7140 //===========================================================================================================================
7141 // DNSServerCmdSigTermHandler
7142 //===========================================================================================================================
7143
7144 static void DNSServerCmdSigTermHandler( void *inContext )
7145 {
7146 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGTERM );
7147 }
7148
7149 #if( TARGET_OS_DARWIN )
7150 //===========================================================================================================================
7151 // DNSServerCmdFollowedProcessHandler
7152 //===========================================================================================================================
7153
7154 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7155 {
7156 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7157
7158 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( context, 0 );
7159 }
7160 #endif
7161
7162 //===========================================================================================================================
7163 // _DNSServerCmdExternalExit
7164 //===========================================================================================================================
7165
7166 #define SignalNumberToString( X ) ( \
7167 ( (X) == SIGINT ) ? "SIGINT" : \
7168 ( (X) == SIGTERM ) ? "SIGTERM" : \
7169 "???" )
7170
7171 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal )
7172 {
7173 dispatch_source_forget( &inContext->sigIntSource );
7174 dispatch_source_forget( &inContext->sigTermSource );
7175 #if( TARGET_OS_DARWIN )
7176 dispatch_source_forget( &inContext->processMonitor );
7177
7178 if( inSignal == 0 )
7179 {
7180 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7181 }
7182 else
7183 #endif
7184 {
7185 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7186 }
7187
7188 ForgetDNSServer( &inContext->server );
7189 }
7190
7191 //===========================================================================================================================
7192 // DNSServerCreate
7193 //===========================================================================================================================
7194
7195 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7196
7197 typedef struct DNSDelayedResponse DNSDelayedResponse;
7198 struct DNSDelayedResponse
7199 {
7200 DNSDelayedResponse * next;
7201 sockaddr_ip destAddr;
7202 uint64_t targetTicks;
7203 uint8_t * msgPtr;
7204 size_t msgLen;
7205 };
7206
7207 struct DNSServerPrivate
7208 {
7209 CFRuntimeBase base; // CF object base.
7210 uint8_t * domain; // Parent domain of server's resource records.
7211 dispatch_queue_t queue; // Queue for DNS server's events.
7212 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7213 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7214 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7215 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7216 SocketRef sockUDPv4;
7217 SocketRef sockUDPv6;
7218 DNSServerEventHandler_f eventHandler;
7219 void * eventContext;
7220 DNSDelayedResponse * responseList;
7221 dispatch_source_t responseTimer;
7222 unsigned int responseDelayMs;
7223 uint32_t defaultTTL;
7224 uint32_t serial; // Serial number for SOA record.
7225 int port; // Port to use for receiving and sending DNS messages.
7226 OSStatus stopError;
7227 Boolean stopped;
7228 Boolean loopbackOnly;
7229 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
7230 };
7231
7232 static void _DNSServerUDPReadHandler( void *inContext );
7233 static void _DNSServerTCPReadHandler( void *inContext );
7234 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse );
7235 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList );
7236
7237 CF_CLASS_DEFINE( DNSServer );
7238
7239 static OSStatus
7240 DNSServerCreate(
7241 dispatch_queue_t inQueue,
7242 DNSServerEventHandler_f inEventHandler,
7243 void * inEventContext,
7244 unsigned int inResponseDelayMs,
7245 uint32_t inDefaultTTL,
7246 int inPort,
7247 Boolean inLoopbackOnly,
7248 const char * inDomain,
7249 Boolean inBadUDPMode,
7250 DNSServerRef * outServer )
7251 {
7252 OSStatus err;
7253 DNSServerRef obj = NULL;
7254
7255 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
7256
7257 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7258
7259 ReplaceDispatchQueue( &obj->queue, inQueue );
7260 obj->eventHandler = inEventHandler;
7261 obj->eventContext = inEventContext;
7262 obj->responseDelayMs = inResponseDelayMs;
7263 obj->defaultTTL = inDefaultTTL;
7264 obj->port = inPort;
7265 obj->loopbackOnly = inLoopbackOnly;
7266 obj->badUDPMode = inBadUDPMode;
7267
7268 if( inDomain )
7269 {
7270 err = StringToDomainName( inDomain, &obj->domain, NULL );
7271 require_noerr_quiet( err, exit );
7272 }
7273 else
7274 {
7275 err = DomainNameDup( kDDotTestDomainName, &obj->domain, NULL );
7276 require_noerr_quiet( err, exit );
7277 }
7278
7279 *outServer = obj;
7280 obj = NULL;
7281 err = kNoErr;
7282
7283 exit:
7284 CFReleaseNullSafe( obj );
7285 return( err );
7286 }
7287
7288 //===========================================================================================================================
7289 // _DNSServerFinalize
7290 //===========================================================================================================================
7291
7292 static void _DNSServerFinalize( CFTypeRef inObj )
7293 {
7294 DNSServerRef const me = (DNSServerRef) inObj;
7295
7296 check( !me->readSourceUDPv4 );
7297 check( !me->readSourceUDPv6 );
7298 check( !me->readSourceTCPv4 );
7299 check( !me->readSourceTCPv6 );
7300 check( !me->responseTimer );
7301 ForgetMem( &me->domain );
7302 dispatch_forget( &me->queue );
7303 }
7304
7305 //===========================================================================================================================
7306 // DNSServerStart
7307 //===========================================================================================================================
7308
7309 static void _DNSServerStart( void *inContext );
7310 static void _DNSServerStop( void *inContext, OSStatus inError );
7311
7312 static void DNSServerStart( DNSServerRef me )
7313 {
7314 CFRetain( me );
7315 dispatch_async_f( me->queue, me, _DNSServerStart );
7316 }
7317
7318 static void _DNSServerStart( void *inContext )
7319 {
7320 OSStatus err;
7321 struct timeval now;
7322 DNSServerRef const me = (DNSServerRef) inContext;
7323 SocketRef sock = kInvalidSocketRef;
7324 SocketContext * sockCtx = NULL;
7325 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7326 int year, month, day;
7327
7328 // Create IPv4 UDP socket.
7329 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7330 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7331 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7332 // used for the remaining sockets.
7333
7334 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7335 me->port, &me->port, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7336 require_noerr( err, exit );
7337 check( me->port > 0 );
7338
7339 // Create read source for IPv4 UDP socket.
7340
7341 err = SocketContextCreate( sock, me, &sockCtx );
7342 require_noerr( err, exit );
7343 sock = kInvalidSocketRef;
7344
7345 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7346 &me->readSourceUDPv4 );
7347 require_noerr( err, exit );
7348 dispatch_resume( me->readSourceUDPv4 );
7349 me->sockUDPv4 = sockCtx->sock;
7350 sockCtx = NULL;
7351
7352 // Create IPv6 UDP socket.
7353
7354 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7355 me->port, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7356 require_noerr( err, exit );
7357
7358 // Create read source for IPv6 UDP socket.
7359
7360 err = SocketContextCreate( sock, me, &sockCtx );
7361 require_noerr( err, exit );
7362 sock = kInvalidSocketRef;
7363
7364 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7365 &me->readSourceUDPv6 );
7366 require_noerr( err, exit );
7367 dispatch_resume( me->readSourceUDPv6 );
7368 me->sockUDPv6 = sockCtx->sock;
7369 sockCtx = NULL;
7370
7371 // Create IPv4 TCP socket.
7372
7373 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7374 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7375 require_noerr( err, exit );
7376
7377 // Create read source for IPv4 TCP socket.
7378
7379 err = SocketContextCreate( sock, me, &sockCtx );
7380 require_noerr( err, exit );
7381 sock = kInvalidSocketRef;
7382
7383 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7384 &me->readSourceTCPv4 );
7385 require_noerr( err, exit );
7386 dispatch_resume( me->readSourceTCPv4 );
7387 sockCtx = NULL;
7388
7389 // Create IPv6 TCP socket.
7390
7391 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7392 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7393 require_noerr( err, exit );
7394
7395 // Create read source for IPv6 TCP socket.
7396
7397 err = SocketContextCreate( sock, me, &sockCtx );
7398 require_noerr( err, exit );
7399 sock = kInvalidSocketRef;
7400
7401 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7402 &me->readSourceTCPv6 );
7403 require_noerr( err, exit );
7404 dispatch_resume( me->readSourceTCPv6 );
7405 sockCtx = NULL;
7406
7407 ds_ulog( kLogLevelInfo, "Server is using port %d.\n", me->port );
7408 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, (uintptr_t) me->port, me->eventContext );
7409
7410 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7411 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7412
7413 gettimeofday( &now, NULL );
7414 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
7415 NULL, NULL, NULL );
7416 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
7417
7418 exit:
7419 ForgetSocket( &sock );
7420 if( sockCtx ) SocketContextRelease( sockCtx );
7421 if( err ) _DNSServerStop( me, err );
7422 }
7423
7424 //===========================================================================================================================
7425 // DNSServerStop
7426 //===========================================================================================================================
7427
7428 static void _DNSServerUserStop( void *inContext );
7429 static void _DNSServerStop2( void *inContext );
7430
7431 static void DNSServerStop( DNSServerRef me )
7432 {
7433 CFRetain( me );
7434 dispatch_async_f( me->queue, me, _DNSServerUserStop );
7435 }
7436
7437 static void _DNSServerUserStop( void *inContext )
7438 {
7439 DNSServerRef const me = (DNSServerRef) inContext;
7440
7441 _DNSServerStop( me, kNoErr );
7442 CFRelease( me );
7443 }
7444
7445 static void _DNSServerStop( void *inContext, OSStatus inError )
7446 {
7447 DNSServerRef const me = (DNSServerRef) inContext;
7448
7449 me->stopError = inError;
7450 dispatch_source_forget( &me->readSourceUDPv4 );
7451 dispatch_source_forget( &me->readSourceUDPv6 );
7452 dispatch_source_forget( &me->readSourceTCPv4 );
7453 dispatch_source_forget( &me->readSourceTCPv6 );
7454 dispatch_source_forget( &me->responseTimer );
7455 me->sockUDPv4 = kInvalidSocketRef;
7456 me->sockUDPv6 = kInvalidSocketRef;
7457
7458 if( me->responseList )
7459 {
7460 _DNSDelayedResponseFreeList( me->responseList );
7461 me->responseList = NULL;
7462 }
7463 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7464 }
7465
7466 static void _DNSServerStop2( void *inContext )
7467 {
7468 DNSServerRef const me = (DNSServerRef) inContext;
7469
7470 if( !me->stopped )
7471 {
7472 me->stopped = true;
7473 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, (uintptr_t) me->stopError, me->eventContext );
7474 CFRelease( me );
7475 }
7476 CFRelease( me );
7477 }
7478
7479 //===========================================================================================================================
7480 // _DNSDelayedResponseFree
7481 //===========================================================================================================================
7482
7483 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse )
7484 {
7485 ForgetMem( &inResponse->msgPtr );
7486 free( inResponse );
7487 }
7488
7489 //===========================================================================================================================
7490 // _DNSDelayedResponseFreeList
7491 //===========================================================================================================================
7492
7493 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList )
7494 {
7495 DNSDelayedResponse * response;
7496
7497 while( ( response = inList ) != NULL )
7498 {
7499 inList = response->next;
7500 _DNSDelayedResponseFree( response );
7501 }
7502 }
7503
7504 //===========================================================================================================================
7505 // _DNSServerUDPReadHandler
7506 //===========================================================================================================================
7507
7508 static OSStatus
7509 _DNSServerAnswerQuery(
7510 DNSServerRef inServer,
7511 const uint8_t * inQueryPtr,
7512 size_t inQueryLen,
7513 Boolean inForTCP,
7514 uint8_t ** outResponsePtr,
7515 size_t * outResponseLen );
7516
7517 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7518 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7519
7520 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7521 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7522
7523 static OSStatus
7524 _DNSServerScheduleDelayedResponse(
7525 DNSServerRef inServer,
7526 const struct sockaddr * inDestAddr,
7527 uint8_t * inMsgPtr,
7528 size_t inMsgLen );
7529 static void _DNSServerUDPDelayedSend( void *inContext );
7530
7531 static void _DNSServerUDPReadHandler( void *inContext )
7532 {
7533 OSStatus err;
7534 SocketContext * const sockCtx = (SocketContext *) inContext;
7535 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7536 struct timeval now;
7537 ssize_t n;
7538 sockaddr_ip clientAddr;
7539 socklen_t clientAddrLen;
7540 uint8_t * responsePtr = NULL; // malloc'd
7541 size_t responseLen;
7542 uint8_t msg[ 512 ];
7543
7544 gettimeofday( &now, NULL );
7545
7546 // Receive message.
7547
7548 clientAddrLen = (socklen_t) sizeof( clientAddr );
7549 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7550 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7551 require_noerr( err, exit );
7552
7553 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7554
7555 if( n < kDNSHeaderLength )
7556 {
7557 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7558 goto exit;
7559 }
7560
7561 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7562
7563 // Create response.
7564
7565 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, &responsePtr, &responseLen );
7566 require_noerr_quiet( err, exit );
7567
7568 // Schedule response.
7569
7570 if( me->responseDelayMs > 0 )
7571 {
7572 err = _DNSServerScheduleDelayedResponse( me, &clientAddr.sa, responsePtr, responseLen );
7573 require_noerr( err, exit );
7574 responsePtr = NULL;
7575 }
7576 else
7577 {
7578 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7579
7580 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7581 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7582 require_noerr( err, exit );
7583 }
7584
7585 exit:
7586 FreeNullSafe( responsePtr );
7587 return;
7588 }
7589
7590 static OSStatus
7591 _DNSServerScheduleDelayedResponse(
7592 DNSServerRef me,
7593 const struct sockaddr * inDestAddr,
7594 uint8_t * inMsgPtr,
7595 size_t inMsgLen )
7596 {
7597 OSStatus err;
7598 DNSDelayedResponse * response;
7599 DNSDelayedResponse ** responsePtr;
7600 DNSDelayedResponse * newResponse;
7601 uint64_t targetTicks;
7602
7603 targetTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
7604
7605 newResponse = (DNSDelayedResponse *) calloc( 1, sizeof( *newResponse ) );
7606 require_action( newResponse, exit, err = kNoMemoryErr );
7607
7608 if( !me->responseList || ( targetTicks < me->responseList->targetTicks ) )
7609 {
7610 dispatch_source_forget( &me->responseTimer );
7611
7612 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7613 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, _DNSServerUDPDelayedSend,
7614 NULL, me, &me->responseTimer );
7615 require_noerr( err, exit );
7616 dispatch_resume( me->responseTimer );
7617 }
7618
7619 SockAddrCopy( inDestAddr, &newResponse->destAddr );
7620 newResponse->targetTicks = targetTicks;
7621 newResponse->msgPtr = inMsgPtr;
7622 newResponse->msgLen = inMsgLen;
7623
7624 for( responsePtr = &me->responseList; ( response = *responsePtr ) != NULL; responsePtr = &response->next )
7625 {
7626 if( newResponse->targetTicks < response->targetTicks ) break;
7627 }
7628 newResponse->next = response;
7629 *responsePtr = newResponse;
7630 newResponse = NULL;
7631 err = kNoErr;
7632
7633 exit:
7634 if( newResponse ) _DNSDelayedResponseFree( newResponse );
7635 return( err );
7636 }
7637
7638 static void _DNSServerUDPDelayedSend( void *inContext )
7639 {
7640 OSStatus err;
7641 DNSServerRef const me = (DNSServerRef) inContext;
7642 DNSDelayedResponse * response;
7643 SocketRef sock;
7644 ssize_t n;
7645 uint64_t nowTicks;
7646 uint64_t remainingNs;
7647 DNSDelayedResponse * freeList = NULL;
7648
7649 dispatch_source_forget( &me->responseTimer );
7650
7651 nowTicks = UpTicks();
7652 while( ( ( response = me->responseList ) != NULL ) && ( response->targetTicks <= nowTicks ) )
7653 {
7654 me->responseList = response->next;
7655
7656 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7657 response->msgLen, response->msgPtr, response->msgLen );
7658
7659 sock = ( response->destAddr.sa.sa_family == AF_INET ) ? me->sockUDPv4 : me->sockUDPv6;
7660 n = sendto( sock, (char *) response->msgPtr, response->msgLen, 0, &response->destAddr.sa,
7661 SockAddrGetSize( &response->destAddr ) );
7662 err = map_socket_value_errno( sock, n == (ssize_t) response->msgLen, n );
7663 check_noerr( err );
7664
7665 response->next = freeList;
7666 freeList = response;
7667 nowTicks = UpTicks();
7668 }
7669
7670 if( response )
7671 {
7672 check( response->targetTicks > nowTicks );
7673 remainingNs = UpTicksToNanoseconds( response->targetTicks - nowTicks );
7674 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7675
7676 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7677 me->queue, _DNSServerUDPDelayedSend, NULL, me, &me->responseTimer );
7678 require_noerr( err, exit );
7679 dispatch_resume( me->responseTimer );
7680 }
7681
7682 exit:
7683 if( freeList ) _DNSDelayedResponseFreeList( freeList );
7684 }
7685
7686 //===========================================================================================================================
7687 // _DNSServerAnswerQuery
7688 //===========================================================================================================================
7689
7690 #define kLabelPrefix_Alias "alias"
7691 #define kLabelPrefix_AliasTTL "alias-ttl"
7692 #define kLabelPrefix_Count "count-"
7693 #define kLabelPrefix_Tag "tag-"
7694 #define kLabelPrefix_TTL "ttl-"
7695 #define kLabel_IPv4 "ipv4"
7696 #define kLabel_IPv6 "ipv6"
7697 #define kLabelPrefix_SRV "srv-"
7698
7699 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7700 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7701
7702 typedef struct
7703 {
7704 uint16_t priority; // Priority from SRV label.
7705 uint16_t weight; // Weight from SRV label.
7706 uint16_t port; // Port number from SRV label.
7707 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
7708 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
7709
7710 } ParsedSRV;
7711
7712 static OSStatus
7713 _DNSServerInitializeResponseMessage(
7714 DataBuffer * inDB,
7715 unsigned int inID,
7716 unsigned int inFlags,
7717 const uint8_t * inQName,
7718 unsigned int inQType,
7719 unsigned int inQClass );
7720 static OSStatus
7721 _DNSServerAnswerQueryDynamically(
7722 DNSServerRef inServer,
7723 const uint8_t * inQName,
7724 unsigned int inQType,
7725 unsigned int inQClass,
7726 Boolean inForTCP,
7727 DataBuffer * inDB );
7728 static Boolean
7729 _DNSServerNameIsSRVName(
7730 DNSServerRef inServer,
7731 const uint8_t * inName,
7732 const uint8_t ** outDomainPtr,
7733 size_t * outDomainLen,
7734 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
7735 size_t * outSRVCount );
7736 static Boolean
7737 _DNSServerNameIsHostname(
7738 DNSServerRef inServer,
7739 const uint8_t * inName,
7740 uint32_t * outAliasCount,
7741 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
7742 size_t * outAliasTTLCount,
7743 unsigned int * outCount,
7744 unsigned int * outRandCount,
7745 uint32_t * outTTL,
7746 Boolean * outHasA,
7747 Boolean * outHasAAAA,
7748 Boolean * outHasSOA );
7749
7750 static OSStatus
7751 _DNSServerAnswerQuery(
7752 DNSServerRef me,
7753 const uint8_t * const inQueryPtr,
7754 const size_t inQueryLen,
7755 Boolean inForTCP,
7756 uint8_t ** outResponsePtr,
7757 size_t * outResponseLen )
7758 {
7759 OSStatus err;
7760 DataBuffer dataBuf;
7761 const uint8_t * ptr;
7762 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
7763 const DNSHeader * qhdr;
7764 const dns_fixed_fields_question * fields;
7765 unsigned int msgID, qflags, qtype, qclass, rflags;
7766 uint8_t qname[ kDomainNameLengthMax ];
7767
7768 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
7769
7770 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
7771
7772 qhdr = (const DNSHeader *) inQueryPtr;
7773 msgID = DNSHeaderGetID( qhdr );
7774 qflags = DNSHeaderGetFlags( qhdr );
7775
7776 // Minimal checking of the query message's header.
7777
7778 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
7779 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
7780 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
7781 {
7782 err = kRequestErr;
7783 goto exit;
7784 }
7785
7786 // Get QNAME.
7787
7788 ptr = (const uint8_t *) &qhdr[ 1 ];
7789 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
7790 require_noerr( err, exit );
7791
7792 // Get QTYPE and QCLASS.
7793
7794 require_action_quiet( ( (size_t)( queryEnd - ptr ) ) >= sizeof( *fields ), exit, err = kUnderrunErr );
7795 fields = (const dns_fixed_fields_question *) ptr;
7796 qtype = dns_fixed_fields_question_get_type( fields );
7797 qclass = dns_fixed_fields_question_get_class( fields );
7798
7799 // Create a tentative response message.
7800
7801 rflags = kDNSHeaderFlag_Response;
7802 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
7803 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
7804
7805 if( me->badUDPMode && !inForTCP ) msgID = (uint16_t)( msgID + 1 );
7806 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7807 require_noerr( err, exit );
7808
7809 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inForTCP, &dataBuf );
7810 if( err )
7811 {
7812 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
7813 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7814 require_noerr( err, exit );
7815 }
7816
7817 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
7818 require_noerr( err, exit );
7819
7820 exit:
7821 DataBuffer_Free( &dataBuf );
7822 return( err );
7823 }
7824
7825 static OSStatus
7826 _DNSServerInitializeResponseMessage(
7827 DataBuffer * inDB,
7828 unsigned int inID,
7829 unsigned int inFlags,
7830 const uint8_t * inQName,
7831 unsigned int inQType,
7832 unsigned int inQClass )
7833 {
7834 OSStatus err;
7835 DNSHeader header;
7836
7837 DataBuffer_Reset( inDB );
7838
7839 memset( &header, 0, sizeof( header ) );
7840 DNSHeaderSetID( &header, inID );
7841 DNSHeaderSetFlags( &header, inFlags );
7842 DNSHeaderSetQuestionCount( &header, 1 );
7843
7844 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
7845 require_noerr( err, exit );
7846
7847 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), (uint16_t) inQType,
7848 (uint16_t) inQClass );
7849 require_noerr( err, exit );
7850
7851 exit:
7852 return( err );
7853 }
7854
7855 static OSStatus
7856 _DNSServerAnswerQueryDynamically(
7857 DNSServerRef me,
7858 const uint8_t * const inQName,
7859 const unsigned int inQType,
7860 const unsigned int inQClass,
7861 const Boolean inForTCP,
7862 DataBuffer * const inDB )
7863 {
7864 OSStatus err;
7865 DNSHeader * hdr;
7866 unsigned int flags, rcode;
7867 uint32_t aliasCount, i;
7868 uint32_t aliasTTLs[ kMaxAliasTTLCount ];
7869 size_t aliasTTLCount;
7870 unsigned int addrCount, randCount;
7871 uint32_t ttl;
7872 ParsedSRV srvArray[ kMaxParsedSRVCount ];
7873 size_t srvCount;
7874 const uint8_t * srvDomainPtr;
7875 size_t srvDomainLen;
7876 unsigned int answerCount;
7877 Boolean notImplemented, truncated;
7878 Boolean useAliasTTLs, nameExists, nameHasA, nameHasAAAA, nameHasSRV, nameHasSOA;
7879 uint8_t namePtr[ 2 ];
7880 dns_fixed_fields_record fields;
7881
7882 answerCount = 0;
7883 truncated = false;
7884 nameExists = false;
7885 require_action_quiet( inQClass == kDNSServiceClass_IN, done, notImplemented = true );
7886
7887 notImplemented = false;
7888 aliasCount = 0;
7889 nameHasA = false;
7890 nameHasAAAA = false;
7891 nameHasSOA = false;
7892 useAliasTTLs = false;
7893 nameHasSRV = false;
7894 srvDomainLen = 0;
7895 srvCount = 0;
7896
7897 if( _DNSServerNameIsHostname( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount, &ttl,
7898 &nameHasA, &nameHasAAAA, &nameHasSOA ) )
7899 {
7900 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
7901 check( ( addrCount >= 1 ) && ( addrCount <= 255 ) );
7902 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
7903 check( nameHasA || nameHasAAAA );
7904
7905 if( aliasTTLCount > 0 )
7906 {
7907 aliasCount = (uint32_t) aliasTTLCount;
7908 useAliasTTLs = true;
7909 }
7910 nameExists = true;
7911 }
7912 else if( _DNSServerNameIsSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) )
7913 {
7914 nameHasSRV = true;
7915 nameExists = true;
7916 }
7917 require_quiet( nameExists, done );
7918
7919 if( aliasCount > 0 )
7920 {
7921 size_t nameOffset;
7922 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
7923
7924 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7925 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7926 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7927 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7928
7929 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
7930
7931 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7932
7933 nameOffset = kDNSHeaderLength;
7934
7935 for( i = aliasCount; i >= 1; --i )
7936 {
7937 size_t nameLen;
7938 size_t rdataLen;
7939 uint32_t j;
7940 uint32_t aliasTTL;
7941 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
7942
7943 if( nameOffset <= kDNSCompressionOffsetMax )
7944 {
7945 DNSMessageWriteLabelPointer( namePtr, nameOffset );
7946 nameLen = sizeof( namePtr );
7947 }
7948 else
7949 {
7950 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
7951 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
7952 }
7953
7954 if( i >= 2 )
7955 {
7956 char * dst = (char *) &rdataLabel[ 1 ];
7957 char * const lim = (char *) &rdataLabel[ countof( rdataLabel ) ];
7958
7959 if( useAliasTTLs )
7960 {
7961 err = SNPrintF_Add( &dst, lim, kLabelPrefix_AliasTTL );
7962 require_noerr( err, exit );
7963
7964 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
7965 {
7966 err = SNPrintF_Add( &dst, lim, "-%u", aliasTTLs[ j ] );
7967 require_noerr( err, exit );
7968 }
7969 }
7970 else
7971 {
7972 err = SNPrintF_Add( &dst, lim, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
7973 require_noerr( err, exit );
7974 }
7975 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
7976 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
7977 }
7978 else
7979 {
7980 rdataLen = sizeof( superPtr );
7981 }
7982
7983 if( !inForTCP )
7984 {
7985 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
7986
7987 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
7988 {
7989 truncated = true;
7990 goto done;
7991 }
7992 }
7993 ++answerCount;
7994
7995 // Set CNAME record's NAME.
7996
7997 if( nameOffset <= kDNSCompressionOffsetMax )
7998 {
7999 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8000 require_noerr( err, exit );
8001 }
8002 else
8003 {
8004 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
8005 require_noerr( err, exit );
8006
8007 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8008 require_noerr( err, exit );
8009 }
8010
8011 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8012
8013 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
8014 dns_fixed_fields_record_init( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
8015 (uint16_t) rdataLen );
8016 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8017 require_noerr( err, exit );
8018
8019 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8020
8021 nameOffset = DataBuffer_GetLen( inDB );
8022
8023 // Set CNAME record's RDATA.
8024
8025 if( i >= 2 )
8026 {
8027 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
8028 require_noerr( err, exit );
8029 }
8030 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8031 require_noerr( err, exit );
8032 }
8033
8034 namePtr[ 0 ] = superPtr[ 0 ];
8035 namePtr[ 1 ] = superPtr[ 1 ];
8036 }
8037 else
8038 {
8039 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8040
8041 DNSMessageWriteLabelPointer( namePtr, kDNSHeaderLength );
8042 }
8043
8044 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
8045 {
8046 uint8_t * lsb; // Pointer to the least significant byte of record data.
8047 size_t recordLen; // Length of the entire record.
8048 size_t rdataLen; // Length of record's RDATA.
8049 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8050 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
8051 const int useBadAddrs = ( me->badUDPMode && !inForTCP ) ? true : false;
8052
8053 if( inQType == kDNSServiceType_A )
8054 {
8055 const uint32_t baseAddrV4 = useBadAddrs ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
8056
8057 require_quiet( nameHasA, done );
8058
8059 rdataLen = 4;
8060 WriteBig32( rdata, baseAddrV4 );
8061 lsb = &rdata[ 3 ];
8062 }
8063 else
8064 {
8065 const uint8_t * const baseAddrV6 = useBadAddrs ? kDNSServerBadBaseAddrV6 : kDNSServerBaseAddrV6;
8066
8067 require_quiet( nameHasAAAA, done );
8068
8069 rdataLen = 16;
8070 memcpy( rdata, baseAddrV6, 16 );
8071 lsb = &rdata[ 15 ];
8072 }
8073
8074 if( randCount > 0 )
8075 {
8076 // Populate the array with all integers between 1 and <randCount>, inclusive.
8077
8078 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
8079
8080 // Prevent dubious static analyzer warning.
8081 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8082 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8083 // for some reason.
8084
8085 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
8086 addrCount, randCount );
8087
8088 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8089 // 1 and <randCount>, inclusive.
8090 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8091 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8092
8093 for( i = 0; i < addrCount; ++i )
8094 {
8095 uint8_t tmp;
8096 uint32_t j;
8097
8098 j = RandomRange( i, randCount - 1 );
8099 if( i != j )
8100 {
8101 tmp = randIntegers[ i ];
8102 randIntegers[ i ] = randIntegers[ j ];
8103 randIntegers[ j ] = tmp;
8104 }
8105 }
8106 }
8107
8108 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8109 for( i = 0; i < addrCount; ++i )
8110 {
8111 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8112 {
8113 truncated = true;
8114 goto done;
8115 }
8116
8117 // Set record NAME.
8118
8119 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8120 require_noerr( err, exit );
8121
8122 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8123
8124 dns_fixed_fields_record_init( &fields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
8125 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8126 require_noerr( err, exit );
8127
8128 // Set record RDATA.
8129
8130 *lsb = ( randCount > 0 ) ? randIntegers[ i ] : ( *lsb + 1 );
8131
8132 err = DataBuffer_Append( inDB, rdata, rdataLen );
8133 require_noerr( err, exit );
8134
8135 ++answerCount;
8136 }
8137 }
8138 else if( inQType == kDNSServiceType_SRV )
8139 {
8140 require_quiet( nameHasSRV, done );
8141
8142 dns_fixed_fields_record_init( &fields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
8143
8144 for( i = 0; i < srvCount; ++i )
8145 {
8146 dns_fixed_fields_srv fieldsSRV;
8147 size_t rdataLen;
8148 size_t recordLen;
8149 const ParsedSRV * const srv = &srvArray[ i ];
8150
8151 rdataLen = sizeof( fieldsSRV ) + srvDomainLen + srv->targetLen + 1;
8152 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8153
8154 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8155 {
8156 truncated = true;
8157 goto done;
8158 }
8159
8160 // Append record NAME.
8161
8162 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8163 require_noerr( err, exit );
8164
8165 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8166
8167 WriteBig16( fields.rdlength, rdataLen );
8168 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8169 require_noerr( err, exit );
8170
8171 // Append SRV RDATA.
8172
8173 dns_fixed_fields_srv_init( &fieldsSRV, srv->priority, srv->weight, srv->port );
8174
8175 err = DataBuffer_Append( inDB, &fieldsSRV, sizeof( fieldsSRV ) );
8176 require_noerr( err, exit );
8177
8178 if( srv->targetLen > 0 )
8179 {
8180 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
8181 require_noerr( err, exit );
8182 }
8183
8184 if( srvDomainLen > 0 )
8185 {
8186 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
8187 require_noerr( err, exit );
8188 }
8189
8190 err = DataBuffer_Append( inDB, "", 1 ); // Append root label.
8191 require_noerr( err, exit );
8192
8193 ++answerCount;
8194 }
8195 }
8196 else if( inQType == kDNSServiceType_SOA )
8197 {
8198 size_t nameLen, recordLen;
8199
8200 require_quiet( nameHasSOA, done );
8201
8202 nameLen = DomainNameLength( me->domain );
8203 if( !inForTCP )
8204 {
8205 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
8206 require_noerr( err, exit );
8207
8208 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8209 {
8210 truncated = true;
8211 goto done;
8212 }
8213 }
8214
8215 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
8216 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
8217 me->defaultTTL, NULL );
8218 require_noerr( err, exit );
8219
8220 ++answerCount;
8221 }
8222
8223 done:
8224 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8225 flags = DNSHeaderGetFlags( hdr );
8226 if( notImplemented )
8227 {
8228 rcode = kDNSRCode_NotImplemented;
8229 }
8230 else
8231 {
8232 flags |= kDNSHeaderFlag_AuthAnswer;
8233 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8234 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8235 }
8236 DNSFlagsSetRCode( flags, rcode );
8237 DNSHeaderSetFlags( hdr, flags );
8238 DNSHeaderSetAnswerCount( hdr, answerCount );
8239 err = kNoErr;
8240
8241 exit:
8242 return( err );
8243 }
8244
8245 static Boolean
8246 _DNSServerNameIsHostname(
8247 DNSServerRef me,
8248 const uint8_t * inName,
8249 uint32_t * outAliasCount,
8250 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
8251 size_t * outAliasTTLCount,
8252 unsigned int * outCount,
8253 unsigned int * outRandCount,
8254 uint32_t * outTTL,
8255 Boolean * outHasA,
8256 Boolean * outHasAAAA,
8257 Boolean * outHasSOA )
8258 {
8259 OSStatus err;
8260 const uint8_t * label;
8261 const uint8_t * nextLabel;
8262 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8263 unsigned int count = 0; // First arg from Count label. Valid values are in [1, 255].
8264 unsigned int randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
8265 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8266 size_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
8267 int hasTagLabel = false;
8268 int hasIPv4Label = false;
8269 int hasIPv6Label = false;
8270 int isNameValid = false;
8271
8272 for( label = inName; label[ 0 ]; label = nextLabel )
8273 {
8274 uint32_t arg;
8275
8276 nextLabel = &label[ 1 + label[ 0 ] ];
8277
8278 // Check if the first label is a valid alias TTL sequence label.
8279
8280 if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_AliasTTL ) == 0 ) )
8281 {
8282 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_AliasTTL ) ];
8283 const char * const end = (const char *) nextLabel;
8284 const char * next;
8285
8286 check( label[ 0 ] <= kDomainLabelLengthMax );
8287
8288 while( ptr < end )
8289 {
8290 if( *ptr != '-' ) break;
8291 ++ptr;
8292 err = DecimalTextToUInt32( ptr, end, &arg, &next );
8293 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8294 inAliasTTLs[ aliasTTLCount++ ] = arg;
8295 ptr = next;
8296 }
8297 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
8298 }
8299
8300 // Check if the first label is a valid alias label.
8301
8302 else if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Alias ) == 0 ) )
8303 {
8304 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Alias ) ];
8305 const char * const end = (const char *) nextLabel;
8306
8307 if( ptr < end )
8308 {
8309 if( *ptr++ != '-' ) break;
8310 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8311 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
8312 aliasCount = arg;
8313 if( ptr != end ) break;
8314 }
8315 else
8316 {
8317 aliasCount = 1;
8318 }
8319 }
8320
8321 // Check if this label is a valid count label.
8322
8323 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Count ) == 0 )
8324 {
8325 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Count ) ];
8326 const char * const end = (const char *) nextLabel;
8327
8328 if( count > 0 ) break; // Count cannot be specified more than once.
8329
8330 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8331 if( err || ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be in [1, 255].
8332 count = (unsigned int) arg;
8333
8334 if( ptr < end )
8335 {
8336 if( *ptr++ != '-' ) break;
8337 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8338 if( err || ( arg < (uint32_t) count ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
8339 randCount = (unsigned int) arg;
8340 if( ptr != end ) break;
8341 }
8342 }
8343
8344 // Check if this label is a valid TTL label.
8345
8346 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_TTL ) == 0 )
8347 {
8348 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_TTL ) ];
8349 const char * const end = (const char *) nextLabel;
8350
8351 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
8352
8353 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8354 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8355 ttl = (int32_t) arg;
8356 if( ptr != end ) break;
8357 }
8358
8359 // Check if this label is a valid IPv4 label.
8360
8361 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv4 ) == 0 )
8362 {
8363 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8364 hasIPv4Label = true;
8365 }
8366
8367 // Check if this label is a valid IPv6 label.
8368
8369 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv6 ) == 0 )
8370 {
8371 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8372 hasIPv6Label = true;
8373 }
8374
8375 // Check if this label is a valid tag label.
8376
8377 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Tag ) == 0 )
8378 {
8379 hasTagLabel = true;
8380 }
8381
8382 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8383 // In both cases, there are no more labels to check.
8384
8385 else
8386 {
8387 if( DomainNameEqual( label, me->domain ) ) isNameValid = true;
8388 break;
8389 }
8390 }
8391 require_quiet( isNameValid, exit );
8392
8393 if( outAliasCount ) *outAliasCount = aliasCount;
8394 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
8395 if( outCount ) *outCount = ( count > 0 ) ? count : 1;
8396 if( outRandCount ) *outRandCount = randCount;
8397 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
8398 if( outHasA ) *outHasA = ( hasIPv4Label || !hasIPv6Label ) ? true : false;
8399 if( outHasAAAA ) *outHasAAAA = ( hasIPv6Label || !hasIPv4Label ) ? true : false;
8400 if( outHasSOA )
8401 {
8402 *outHasSOA = ( !count && ( ttl < 0 ) && !hasIPv4Label && !hasIPv6Label && !hasTagLabel ) ? true : false;
8403 }
8404
8405 exit:
8406 return( isNameValid ? true : false );
8407 }
8408
8409 static Boolean
8410 _DNSServerNameIsSRVName(
8411 DNSServerRef me,
8412 const uint8_t * inName,
8413 const uint8_t ** outDomainPtr,
8414 size_t * outDomainLen,
8415 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
8416 size_t * outSRVCount )
8417 {
8418 OSStatus err;
8419 const uint8_t * label;
8420 const uint8_t * domainPtr;
8421 size_t domainLen;
8422 size_t srvCount;
8423 uint32_t arg;
8424 int isNameValid = false;
8425
8426 label = inName;
8427
8428 // Ensure that first label, i.e, the service label, begins with a '_' character.
8429
8430 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8431 label = DomainNameGetNextLabel( label );
8432
8433 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8434
8435 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8436 label = DomainNameGetNextLabel( label );
8437
8438 // Parse the domain name, if any.
8439
8440 domainPtr = label;
8441 while( *label )
8442 {
8443 if( DomainNameEqual( label, me->domain ) ||
8444 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8445 label = DomainNameGetNextLabel( label );
8446 }
8447 require_quiet( *label, exit );
8448
8449 domainLen = (size_t)( label - domainPtr );
8450
8451 // Parse SRV labels, if any.
8452
8453 srvCount = 0;
8454 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
8455 {
8456 const uint8_t * const nextLabel = DomainNameGetNextLabel( label );
8457 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
8458 const char * const end = (const char *) nextLabel;
8459 const uint8_t * target;
8460 unsigned int priority, weight, port;
8461
8462 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8463 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8464 priority = (unsigned int) arg;
8465
8466 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8467 ++ptr;
8468
8469 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8470 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8471 weight = (unsigned int) arg;
8472
8473 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8474 ++ptr;
8475
8476 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8477 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8478 port = (unsigned int) arg;
8479
8480 require_quiet( ptr == end, exit );
8481
8482 target = nextLabel;
8483 for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
8484 {
8485 if( DomainNameEqual( label, me->domain ) ||
8486 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8487 }
8488 require_quiet( *label, exit );
8489
8490 if( inSRVArray )
8491 {
8492 inSRVArray[ srvCount ].priority = (uint16_t) priority;
8493 inSRVArray[ srvCount ].weight = (uint16_t) weight;
8494 inSRVArray[ srvCount ].port = (uint16_t) port;
8495 inSRVArray[ srvCount ].targetPtr = target;
8496 inSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
8497 }
8498 ++srvCount;
8499 }
8500 require_quiet( DomainNameEqual( label, me->domain ), exit );
8501 isNameValid = true;
8502
8503 if( outDomainPtr ) *outDomainPtr = domainPtr;
8504 if( outDomainLen ) *outDomainLen = domainLen;
8505 if( outSRVCount ) *outSRVCount = srvCount;
8506
8507 exit:
8508 return( isNameValid ? true : false );
8509 }
8510
8511 //===========================================================================================================================
8512 // _DNSServerTCPReadHandler
8513 //===========================================================================================================================
8514
8515 typedef struct
8516 {
8517 DNSServerRef server; // Reference to DNS server object.
8518 sockaddr_ip clientAddr; // Client's address.
8519 dispatch_source_t readSource; // Dispatch read source for client socket.
8520 dispatch_source_t writeSource; // Dispatch write source for client socket.
8521 size_t offset; // Offset into receive buffer.
8522 void * msgPtr; // Pointer to dynamically allocated message buffer.
8523 size_t msgLen; // Length of message buffer.
8524 Boolean readSuspended; // True if the read source is currently suspended.
8525 Boolean writeSuspended; // True if the write source is currently suspended.
8526 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8527 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8528 iovec_t iov[ 2 ]; // IO vector for writing response message.
8529 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8530 int iovCount; // Vector count for SocketWriteData().
8531
8532 } TCPConnectionContext;
8533
8534 static void TCPConnectionStop( TCPConnectionContext *inContext );
8535 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8536 static void TCPConnectionReadHandler( void *inContext );
8537 static void TCPConnectionWriteHandler( void *inContext );
8538
8539 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8540
8541 static void _DNSServerTCPReadHandler( void *inContext )
8542 {
8543 OSStatus err;
8544 SocketContext * const sockCtx = (SocketContext *) inContext;
8545 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
8546 TCPConnectionContext * connection;
8547 socklen_t clientAddrLen;
8548 SocketRef newSock = kInvalidSocketRef;
8549 SocketContext * newSockCtx = NULL;
8550
8551 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8552 require_action( connection, exit, err = kNoMemoryErr );
8553
8554 CFRetain( me );
8555 connection->server = me;
8556
8557 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8558 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8559 err = map_socket_creation_errno( newSock );
8560 require_noerr( err, exit );
8561
8562 err = SocketContextCreate( newSock, connection, &newSockCtx );
8563 require_noerr( err, exit );
8564 newSock = kInvalidSocketRef;
8565
8566 err = DispatchReadSourceCreate( newSockCtx->sock, me->queue, TCPConnectionReadHandler, SocketContextCancelHandler,
8567 newSockCtx, &connection->readSource );
8568 require_noerr( err, exit );
8569 SocketContextRetain( newSockCtx );
8570 dispatch_resume( connection->readSource );
8571
8572 err = DispatchWriteSourceCreate( newSockCtx->sock, me->queue, TCPConnectionWriteHandler, SocketContextCancelHandler,
8573 newSockCtx, &connection->writeSource );
8574 require_noerr( err, exit );
8575 SocketContextRetain( newSockCtx );
8576 connection->writeSuspended = true;
8577 connection = NULL;
8578
8579 exit:
8580 ForgetSocket( &newSock );
8581 SocketContextRelease( newSockCtx );
8582 TCPConnectionForget( &connection );
8583 }
8584
8585 //===========================================================================================================================
8586 // TCPConnectionStop
8587 //===========================================================================================================================
8588
8589 static void TCPConnectionStop( TCPConnectionContext *inContext )
8590 {
8591 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8592 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8593 }
8594
8595 //===========================================================================================================================
8596 // TCPConnectionContextFree
8597 //===========================================================================================================================
8598
8599 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8600 {
8601 check( !inContext->readSource );
8602 check( !inContext->writeSource );
8603 ForgetCF( &inContext->server );
8604 ForgetMem( &inContext->msgPtr );
8605 free( inContext );
8606 }
8607
8608 //===========================================================================================================================
8609 // TCPConnectionReadHandler
8610 //===========================================================================================================================
8611
8612 static void TCPConnectionReadHandler( void *inContext )
8613 {
8614 OSStatus err;
8615 SocketContext * const sockCtx = (SocketContext *) inContext;
8616 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8617 struct timeval now;
8618 uint8_t * responsePtr = NULL; // malloc'd
8619 size_t responseLen;
8620
8621 // Receive message length.
8622
8623 if( !connection->receivedLength )
8624 {
8625 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8626 if( err == EWOULDBLOCK ) goto exit;
8627 require_noerr( err, exit );
8628
8629 connection->offset = 0;
8630 connection->msgLen = ReadBig16( connection->lenBuf );
8631 connection->msgPtr = malloc( connection->msgLen );
8632 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8633 connection->receivedLength = true;
8634 }
8635
8636 // Receive message.
8637
8638 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8639 if( err == EWOULDBLOCK ) goto exit;
8640 require_noerr( err, exit );
8641
8642 gettimeofday( &now, NULL );
8643 dispatch_suspend( connection->readSource );
8644 connection->readSuspended = true;
8645
8646 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8647 connection->msgLen, &connection->clientAddr, &now );
8648
8649 if( connection->msgLen < kDNSHeaderLength )
8650 {
8651 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8652 goto exit;
8653 }
8654
8655 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8656
8657 // Create response.
8658
8659 err = _DNSServerAnswerQueryForTCP( connection->server, connection->msgPtr, connection->msgLen, &responsePtr,
8660 &responseLen );
8661 require_noerr_quiet( err, exit );
8662
8663 // Send response.
8664
8665 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8666
8667 free( connection->msgPtr );
8668 connection->msgPtr = responsePtr;
8669 connection->msgLen = responseLen;
8670 responsePtr = NULL;
8671
8672 check( connection->msgLen <= UINT16_MAX );
8673 WriteBig16( connection->lenBuf, connection->msgLen );
8674 connection->iov[ 0 ].iov_base = connection->lenBuf;
8675 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8676 connection->iov[ 1 ].iov_base = connection->msgPtr;
8677 connection->iov[ 1 ].iov_len = connection->msgLen;
8678
8679 connection->iovPtr = connection->iov;
8680 connection->iovCount = 2;
8681
8682 check( connection->writeSuspended );
8683 dispatch_resume( connection->writeSource );
8684 connection->writeSuspended = false;
8685
8686 exit:
8687 FreeNullSafe( responsePtr );
8688 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8689 }
8690
8691 //===========================================================================================================================
8692 // TCPConnectionWriteHandler
8693 //===========================================================================================================================
8694
8695 static void TCPConnectionWriteHandler( void *inContext )
8696 {
8697 OSStatus err;
8698 SocketContext * const sockCtx = (SocketContext *) inContext;
8699 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8700
8701 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8702 if( err == EWOULDBLOCK ) goto exit;
8703 check_noerr( err );
8704
8705 TCPConnectionForget( &connection );
8706
8707 exit:
8708 return;
8709 }
8710
8711 //===========================================================================================================================
8712 // MDNSReplierCmd
8713 //===========================================================================================================================
8714
8715 typedef struct
8716 {
8717 uint8_t * hostname; // Used as the base name for hostnames and service names.
8718 uint8_t * serviceLabel; // Label containing the base service name.
8719 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
8720 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
8721 size_t bitmapCount; // Number of 64-bit bitmaps.
8722 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
8723 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
8724 uint32_t ifIndex; // Index of the interface to run on.
8725 unsigned int recordCountA; // Number of A records per hostname.
8726 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
8727 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8728 double ucastDropRate; // Probability of dropping a unicast response.
8729 double mcastDropRate; // Probability of dropping a multicast query or response.
8730 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8731 Boolean noAdditionals; // True if responses are to not include additional records.
8732 Boolean useIPv4; // True if the replier is to use IPv4.
8733 Boolean useIPv6; // True if the replier is to use IPv6.
8734 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
8735 #if( TARGET_OS_DARWIN )
8736 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
8737 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
8738 #endif
8739
8740 } MDNSReplierContext;
8741
8742 typedef struct MRResourceRecord MRResourceRecord;
8743 struct MRResourceRecord
8744 {
8745 MRResourceRecord * next; // Next item in list.
8746 uint8_t * name; // Resource record name.
8747 uint16_t type; // Resource record type.
8748 uint16_t class; // Resource record class.
8749 uint32_t ttl; // Resource record TTL.
8750 uint16_t rdlength; // Resource record data length.
8751 uint8_t * rdata; // Resource record data.
8752 const uint8_t * target; // For SRV records, pointer to target in RDATA.
8753 };
8754
8755 typedef struct MRNameOffsetItem MRNameOffsetItem;
8756 struct MRNameOffsetItem
8757 {
8758 MRNameOffsetItem * next; // Next item in list.
8759 uint16_t offset; // Offset of domain name in response message.
8760 uint8_t name[ 1 ]; // Variable-length array for domain name.
8761 };
8762
8763 #if( TARGET_OS_DARWIN )
8764 static void _MDNSReplierFollowedProcessHandler( void *inContext );
8765 #endif
8766 static void _MDNSReplierReadHandler( void *inContext );
8767 static OSStatus
8768 _MDNSReplierAnswerQuery(
8769 MDNSReplierContext * inContext,
8770 const uint8_t * inQueryPtr,
8771 size_t inQueryLen,
8772 sockaddr_ip * inSender,
8773 SocketRef inSock,
8774 unsigned int inIndex );
8775 static OSStatus
8776 _MDNSReplierAnswerListAdd(
8777 MDNSReplierContext * inContext,
8778 MRResourceRecord ** inAnswerList,
8779 unsigned int inIndex,
8780 const uint8_t * inName,
8781 unsigned int inType,
8782 unsigned int inClass );
8783 static void
8784 _MDNSReplierAnswerListRemovePTR(
8785 MRResourceRecord ** inAnswerListPtr,
8786 const uint8_t * inName,
8787 const uint8_t * inRData );
8788 static OSStatus
8789 _MDNSReplierSendOrDropResponse(
8790 MDNSReplierContext * inContext,
8791 MRResourceRecord * inAnswerList,
8792 sockaddr_ip * inQuerier,
8793 SocketRef inSock,
8794 unsigned int inIndex,
8795 Boolean inUnicast );
8796 static OSStatus
8797 _MDNSReplierCreateResponse(
8798 MDNSReplierContext * inContext,
8799 MRResourceRecord * inAnswerList,
8800 unsigned int inIndex,
8801 uint8_t ** outResponsePtr,
8802 size_t * outResponseLen );
8803 static OSStatus
8804 _MDNSReplierAppendNameToResponse(
8805 DataBuffer * inResponse,
8806 const uint8_t * inName,
8807 MRNameOffsetItem ** inNameOffsetListPtr );
8808 static Boolean
8809 _MDNSReplierServiceTypeMatch(
8810 const MDNSReplierContext * inContext,
8811 const uint8_t * inName,
8812 unsigned int * outTXTSize,
8813 unsigned int * outCount );
8814 static Boolean
8815 _MDNSReplierServiceInstanceNameMatch(
8816 const MDNSReplierContext * inContext,
8817 const uint8_t * inName,
8818 unsigned int * outIndex,
8819 unsigned int * outTXTSize,
8820 unsigned int * outCount );
8821 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
8822 static Boolean
8823 _MDNSReplierHostnameMatch(
8824 const MDNSReplierContext * inContext,
8825 const uint8_t * inName,
8826 unsigned int * outIndex );
8827 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
8828 static OSStatus
8829 _MRResourceRecordCreate(
8830 uint8_t * inName,
8831 uint16_t inType,
8832 uint16_t inClass,
8833 uint32_t inTTL,
8834 uint16_t inRDLength,
8835 uint8_t * inRData,
8836 MRResourceRecord ** outRecord );
8837 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
8838 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
8839 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
8840 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
8841 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
8842
8843 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
8844 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8845
8846 static void MDNSReplierCmd( void )
8847 {
8848 OSStatus err;
8849 MDNSReplierContext * context;
8850 SocketRef sockV4 = kInvalidSocketRef;
8851 SocketRef sockV6 = kInvalidSocketRef;
8852 const char * ifname;
8853 size_t len;
8854 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
8855 char ifnameBuf[ IF_NAMESIZE + 1 ];
8856
8857 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
8858 require_noerr_quiet( err, exit );
8859
8860 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
8861 require_noerr_quiet( err, exit );
8862
8863 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
8864 require_noerr_quiet( err, exit );
8865
8866 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
8867 require_noerr_quiet( err, exit );
8868
8869 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
8870 require_noerr_quiet( err, exit );
8871
8872 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
8873 require_noerr_quiet( err, exit );
8874
8875 if( gMDNSReplier_Foreground )
8876 {
8877 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8878 }
8879
8880 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
8881 require_action( context, exit, err = kNoMemoryErr );
8882
8883 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
8884 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
8885 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
8886 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
8887 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
8888 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
8889 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
8890 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
8891 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
8892 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
8893
8894 #if( TARGET_OS_DARWIN )
8895 if( gMDNSReplier_FollowPID )
8896 {
8897 context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
8898 if( err || ( context->followPID < 0 ) )
8899 {
8900 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
8901 goto exit;
8902 }
8903
8904 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
8905 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
8906 require_noerr( err, exit );
8907 dispatch_resume( context->processMonitor );
8908 }
8909 else
8910 {
8911 context->followPID = -1;
8912 }
8913 #endif
8914
8915 if( context->maxDropCount > 0 )
8916 {
8917 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
8918 require_action( context->dropCounters, exit, err = kNoMemoryErr );
8919 }
8920
8921 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
8922 require_action( context->bitmaps, exit, err = kNoMemoryErr );
8923
8924 // Create the base hostname label.
8925
8926 len = strlen( gMDNSReplier_Hostname );
8927 if( context->maxInstanceCount > 1 )
8928 {
8929 unsigned int maxInstanceCount, digitCount;
8930
8931 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8932 // "-<instance index>" to the base hostname.
8933
8934 maxInstanceCount = context->maxInstanceCount;
8935 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
8936 len += ( 3 + digitCount );
8937 }
8938
8939 if( len <= kDomainLabelLengthMax )
8940 {
8941 uint8_t * dst = &name[ 1 ];
8942 uint8_t * lim = &name[ countof( name ) ];
8943
8944 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
8945 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8946
8947 err = DomainNameDupLower( name, &context->hostname, NULL );
8948 require_noerr( err, exit );
8949 }
8950 else
8951 {
8952 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8953 gMDNSReplier_Hostname, context->maxInstanceCount );
8954 goto exit;
8955 }
8956
8957 // Create the service label.
8958
8959 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
8960 if( len <= kDomainLabelLengthMax )
8961 {
8962 uint8_t * dst = &name[ 1 ];
8963 uint8_t * lim = &name[ countof( name ) ];
8964
8965 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
8966 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8967
8968 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
8969 require_noerr( err, exit );
8970 }
8971 else
8972 {
8973 FPrintF( stderr, "error: Service type tag is too long.\n" );
8974 goto exit;
8975 }
8976
8977 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
8978 require_noerr_quiet( err, exit );
8979
8980 ifname = if_indextoname( context->ifIndex, ifnameBuf );
8981 require_action( ifname, exit, err = kNameErr );
8982
8983 // Set up IPv4 socket.
8984
8985 if( context->useIPv4 )
8986 {
8987 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
8988 require_noerr( err, exit );
8989 }
8990
8991 // Set up IPv6 socket.
8992
8993 if( context->useIPv6 )
8994 {
8995 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
8996 require_noerr( err, exit );
8997 }
8998
8999 // Create dispatch read sources for socket(s).
9000
9001 if( IsValidSocket( sockV4 ) )
9002 {
9003 SocketContext * sockCtx;
9004
9005 err = SocketContextCreate( sockV4, context, &sockCtx );
9006 require_noerr( err, exit );
9007 sockV4 = kInvalidSocketRef;
9008
9009 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9010 &context->readSourceV4 );
9011 if( err ) ForgetSocketContext( &sockCtx );
9012 require_noerr( err, exit );
9013
9014 dispatch_resume( context->readSourceV4 );
9015 }
9016
9017 if( IsValidSocket( sockV6 ) )
9018 {
9019 SocketContext * sockCtx;
9020
9021 err = SocketContextCreate( sockV6, context, &sockCtx );
9022 require_noerr( err, exit );
9023 sockV6 = kInvalidSocketRef;
9024
9025 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9026 &context->readSourceV6 );
9027 if( err ) ForgetSocketContext( &sockCtx );
9028 require_noerr( err, exit );
9029
9030 dispatch_resume( context->readSourceV6 );
9031 }
9032
9033 dispatch_main();
9034
9035 exit:
9036 ForgetSocket( &sockV4 );
9037 ForgetSocket( &sockV6 );
9038 exit( 1 );
9039 }
9040
9041 #if( TARGET_OS_DARWIN )
9042 //===========================================================================================================================
9043 // _MDNSReplierFollowedProcessHandler
9044 //===========================================================================================================================
9045
9046 static void _MDNSReplierFollowedProcessHandler( void *inContext )
9047 {
9048 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
9049
9050 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
9051 {
9052 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
9053 exit( 0 );
9054 }
9055 }
9056 #endif
9057
9058 //===========================================================================================================================
9059 // _MDNSReplierReadHandler
9060 //===========================================================================================================================
9061
9062 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9063
9064 static void _MDNSReplierReadHandler( void *inContext )
9065 {
9066 OSStatus err;
9067 SocketContext * const sockCtx = (SocketContext *) inContext;
9068 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
9069 size_t msgLen;
9070 sockaddr_ip sender;
9071 const DNSHeader * hdr;
9072 unsigned int flags, questionCount, i, j;
9073 const uint8_t * ptr;
9074 int drop, isMetaQuery;
9075
9076 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
9077 NULL, NULL, NULL, NULL );
9078 require_noerr( err, exit );
9079
9080 if( msgLen < kDNSHeaderLength )
9081 {
9082 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
9083 goto exit;
9084 }
9085
9086 // Perform header field checks.
9087 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9088
9089 hdr = (DNSHeader *) context->msgBuf;
9090 flags = DNSHeaderGetFlags( hdr );
9091 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
9092 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
9093 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
9094
9095 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
9096
9097 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9098 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
9099
9100 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9101
9102 questionCount = DNSHeaderGetQuestionCount( hdr );
9103 require_quiet( questionCount > 0, exit );
9104
9105 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
9106
9107 isMetaQuery = false;
9108 ptr = (const uint8_t *) &hdr[ 1 ];
9109 for( i = 0; i < questionCount; ++i )
9110 {
9111 unsigned int count, index;
9112 uint16_t qtype, qclass;
9113 uint8_t qname[ kDomainNameLengthMax ];
9114
9115 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
9116 require_noerr_quiet( err, exit );
9117
9118 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
9119
9120 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
9121 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
9122 {
9123 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
9124 {
9125 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
9126 }
9127 }
9128 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
9129 {
9130 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
9131 {
9132 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
9133 {
9134 if( count < 64 )
9135 {
9136 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
9137 break;
9138 }
9139 else
9140 {
9141 context->bitmaps[ j ] = ~UINT64_C( 0 );
9142 count -= 64;
9143 }
9144 }
9145 }
9146 }
9147 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
9148 {
9149 isMetaQuery = true;
9150 }
9151 }
9152
9153 // Attempt to answer the query message using selected record sets.
9154
9155 if( isMetaQuery )
9156 {
9157 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
9158 check_noerr( err );
9159 }
9160 if( drop ) goto exit;
9161
9162 for( i = 0; i < context->bitmapCount; ++i )
9163 {
9164 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
9165 {
9166 const uint64_t bitmask = UINT64_C( 1 ) << j;
9167
9168 if( context->bitmaps[ i ] & bitmask )
9169 {
9170 context->bitmaps[ i ] &= ~bitmask;
9171
9172 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
9173 ( i * 64 ) + j + 1 );
9174 check_noerr( err );
9175 }
9176 }
9177 }
9178
9179 exit:
9180 return;
9181 }
9182
9183 //===========================================================================================================================
9184 // _MDNSReplierAnswerQuery
9185 //===========================================================================================================================
9186
9187 static OSStatus
9188 _MDNSReplierAnswerQuery(
9189 MDNSReplierContext * inContext,
9190 const uint8_t * inQueryPtr,
9191 size_t inQueryLen,
9192 sockaddr_ip * inSender,
9193 SocketRef inSock,
9194 unsigned int inIndex )
9195 {
9196 OSStatus err;
9197 const DNSHeader * hdr;
9198 const uint8_t * ptr;
9199 unsigned int questionCount, answerCount, i;
9200 MRResourceRecord * ucastAnswerList = NULL;
9201 MRResourceRecord * mcastAnswerList = NULL;
9202
9203 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9204
9205 // Get answers for questions.
9206
9207 check( inQueryLen >= kDNSHeaderLength );
9208 hdr = (const DNSHeader *) inQueryPtr;
9209 questionCount = DNSHeaderGetQuestionCount( hdr );
9210
9211 ptr = (const uint8_t *) &hdr[ 1 ];
9212 for( i = 0; i < questionCount; ++i )
9213 {
9214 MRResourceRecord ** answerListPtr;
9215 uint16_t qtype, qclass;
9216 uint8_t qname[ kDomainNameLengthMax ];
9217
9218 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
9219 require_noerr_quiet( err, exit );
9220
9221 if( qclass & kQClassUnicastResponseBit )
9222 {
9223 qclass &= ~kQClassUnicastResponseBit;
9224 answerListPtr = &ucastAnswerList;
9225 }
9226 else
9227 {
9228 answerListPtr = &mcastAnswerList;
9229 }
9230
9231 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
9232 require_noerr( err, exit );
9233 }
9234 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9235
9236 // Suppress known answers.
9237 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9238 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9239
9240 answerCount = DNSHeaderGetAnswerCount( hdr );
9241 for( i = 0; i < answerCount; ++i )
9242 {
9243 const uint8_t * rdataPtr;
9244 const uint8_t * recordPtr;
9245 uint16_t type, class;
9246 uint8_t name[ kDomainNameLengthMax ];
9247 uint8_t instance[ kDomainNameLengthMax ];
9248
9249 recordPtr = ptr;
9250 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
9251 require_noerr_quiet( err, exit );
9252
9253 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
9254
9255 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
9256 require_noerr( err, exit );
9257
9258 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
9259 require_noerr_quiet( err, exit );
9260
9261 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
9262 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
9263 }
9264 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9265
9266 // Send or drop responses.
9267
9268 if( ucastAnswerList )
9269 {
9270 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
9271 require_noerr( err, exit );
9272 }
9273
9274 if( mcastAnswerList )
9275 {
9276 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
9277 require_noerr( err, exit );
9278 }
9279 err = kNoErr;
9280
9281 exit:
9282 _MRResourceRecordFreeList( ucastAnswerList );
9283 _MRResourceRecordFreeList( mcastAnswerList );
9284 return( err );
9285 }
9286
9287 //===========================================================================================================================
9288 // _MDNSReplierAnswerListAdd
9289 //===========================================================================================================================
9290
9291 static OSStatus
9292 _MDNSReplierAnswerListAdd(
9293 MDNSReplierContext * inContext,
9294 MRResourceRecord ** inAnswerList,
9295 unsigned int inIndex,
9296 const uint8_t * inName,
9297 unsigned int inType,
9298 unsigned int inClass )
9299 {
9300 OSStatus err;
9301 uint8_t * recordName = NULL;
9302 uint8_t * rdataPtr = NULL;
9303 size_t rdataLen;
9304 MRResourceRecord * answer;
9305 MRResourceRecord ** answerPtr;
9306 const uint8_t * const hostname = inContext->hostname;
9307 unsigned int i;
9308 uint32_t index;
9309 unsigned int count, txtSize;
9310
9311 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9312 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
9313
9314 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9315 {
9316 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
9317 {
9318 err = kNoErr;
9319 goto exit;
9320 }
9321 }
9322
9323 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9324 // instance count are for answering queries about service instances.
9325
9326 if( inIndex == 0 )
9327 {
9328 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
9329 {
9330 int listHasTXT = false;
9331
9332 if( inType == kDNSServiceType_ANY )
9333 {
9334 for( answer = *inAnswerList; answer; answer = answer->next )
9335 {
9336 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
9337 {
9338 listHasTXT = true;
9339 break;
9340 }
9341 }
9342 }
9343
9344 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9345 {
9346 err = DomainNameDupLower( inName, &recordName, NULL );
9347 require_noerr( err, exit );
9348
9349 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
9350 require_noerr( err, exit );
9351
9352 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9353 (uint16_t) rdataLen, rdataPtr, &answer );
9354 require_noerr( err, exit );
9355 recordName = NULL;
9356 rdataPtr = NULL;
9357
9358 *answerPtr = answer;
9359 }
9360 else if( inType == kDNSServiceType_NSEC )
9361 {
9362 err = DomainNameDupLower( inName, &recordName, NULL );
9363 require_noerr( err, exit );
9364
9365 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
9366 require_noerr( err, exit );
9367
9368 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9369 (uint16_t) rdataLen, rdataPtr, &answer );
9370 require_noerr( err, exit );
9371 recordName = NULL;
9372 rdataPtr = NULL;
9373
9374 *answerPtr = answer;
9375 }
9376 }
9377 }
9378 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
9379 {
9380 int listHasA = false;
9381 int listHasAAAA = false;
9382
9383 if( inType == kDNSServiceType_ANY )
9384 {
9385 for( answer = *inAnswerList; answer; answer = answer->next )
9386 {
9387 if( answer->type == kDNSServiceType_A )
9388 {
9389 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
9390 }
9391 else if( answer->type == kDNSServiceType_AAAA )
9392 {
9393 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
9394 }
9395 if( listHasA && listHasAAAA ) break;
9396 }
9397 }
9398
9399 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
9400 {
9401 for( i = 1; i <= inContext->recordCountA; ++i )
9402 {
9403 err = DomainNameDupLower( inName, &recordName, NULL );
9404 require_noerr( err, exit );
9405
9406 rdataLen = 4;
9407 rdataPtr = (uint8_t *) malloc( rdataLen );
9408 require_action( rdataPtr, exit, err = kNoMemoryErr );
9409
9410 rdataPtr[ 0 ] = 0;
9411 WriteBig16( &rdataPtr[ 1 ], inIndex );
9412 rdataPtr[ 3 ] = (uint8_t) i;
9413
9414 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9415 (uint16_t) rdataLen, rdataPtr, &answer );
9416 require_noerr( err, exit );
9417 recordName = NULL;
9418 rdataPtr = NULL;
9419
9420 *answerPtr = answer;
9421 answerPtr = &answer->next;
9422 }
9423 }
9424
9425 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
9426 {
9427 for( i = 1; i <= inContext->recordCountAAAA; ++i )
9428 {
9429 err = DomainNameDupLower( inName, &recordName, NULL );
9430 require_noerr( err, exit );
9431
9432 rdataLen = 16;
9433 rdataPtr = (uint8_t *) _memdup( kMDNSReplierBaseAddrV6, rdataLen );
9434 require_action( rdataPtr, exit, err = kNoMemoryErr );
9435
9436 WriteBig16( &rdataPtr[ 12 ], inIndex );
9437 rdataPtr[ 15 ] = (uint8_t) i;
9438
9439 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9440 (uint16_t) rdataLen, rdataPtr, &answer );
9441 require_noerr( err, exit );
9442 recordName = NULL;
9443 rdataPtr = NULL;
9444
9445 *answerPtr = answer;
9446 answerPtr = &answer->next;
9447 }
9448 }
9449 else if( inType == kDNSServiceType_NSEC )
9450 {
9451 err = DomainNameDupLower( inName, &recordName, NULL );
9452 require_noerr( err, exit );
9453
9454 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
9455 {
9456 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
9457 require_noerr( err, exit );
9458 }
9459 else if( inContext->recordCountA > 0 )
9460 {
9461 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
9462 require_noerr( err, exit );
9463 }
9464 else if( inContext->recordCountAAAA > 0 )
9465 {
9466 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
9467 require_noerr( err, exit );
9468 }
9469 else
9470 {
9471 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
9472 require_noerr( err, exit );
9473 }
9474
9475 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9476 (uint16_t) rdataLen, rdataPtr, &answer );
9477 require_noerr( err, exit );
9478 recordName = NULL;
9479 rdataPtr = NULL;
9480
9481 *answerPtr = answer;
9482 }
9483 }
9484 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
9485 {
9486 int listHasPTR = false;
9487
9488 if( inType == kDNSServiceType_ANY )
9489 {
9490 for( answer = *inAnswerList; answer; answer = answer->next )
9491 {
9492 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
9493 {
9494 listHasPTR = true;
9495 break;
9496 }
9497 }
9498 }
9499
9500 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
9501 {
9502 size_t recordNameLen;
9503 uint8_t * ptr;
9504 uint8_t * lim;
9505
9506 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
9507 require_noerr( err, exit );
9508
9509 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
9510 rdataPtr = (uint8_t *) malloc( rdataLen );
9511 require_action( rdataPtr, exit, err = kNoMemoryErr );
9512
9513 lim = &rdataPtr[ rdataLen ];
9514
9515 ptr = &rdataPtr[ 1 ];
9516 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9517 ptr += hostname[ 0 ];
9518 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
9519 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
9520
9521 check( (size_t)( lim - ptr ) >= recordNameLen );
9522 memcpy( ptr, recordName, recordNameLen );
9523 ptr += recordNameLen;
9524
9525 rdataLen = (size_t)( ptr - rdataPtr );
9526
9527 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9528 (uint16_t) rdataLen, rdataPtr, &answer );
9529 require_noerr( err, exit );
9530 recordName = NULL;
9531 rdataPtr = NULL;
9532
9533 *answerPtr = answer;
9534 }
9535 }
9536 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
9537 ( index == inIndex ) && ( count >= inIndex ) )
9538 {
9539 int listHasSRV = false;
9540 int listHasTXT = false;
9541
9542 if( inType == kDNSServiceType_ANY )
9543 {
9544 for( answer = *inAnswerList; answer; answer = answer->next )
9545 {
9546 if( answer->type == kDNSServiceType_SRV )
9547 {
9548 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
9549 }
9550 else if( answer->type == kDNSServiceType_TXT )
9551 {
9552 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
9553 }
9554 if( listHasSRV && listHasTXT ) break;
9555 }
9556 }
9557
9558 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
9559 {
9560 dns_fixed_fields_srv * fields;
9561 uint8_t * ptr;
9562 uint8_t * lim;
9563 uint8_t * targetPtr;
9564
9565 err = DomainNameDupLower( inName, &recordName, NULL );
9566 require_noerr( err, exit );
9567
9568 rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
9569 rdataPtr = (uint8_t *) malloc( rdataLen );
9570 require_action( rdataPtr, exit, err = kNoMemoryErr );
9571
9572 lim = &rdataPtr[ rdataLen ];
9573
9574 fields = (dns_fixed_fields_srv *) rdataPtr;
9575 dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
9576
9577 targetPtr = (uint8_t *) &fields[ 1 ];
9578
9579 ptr = &targetPtr[ 1 ];
9580 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9581 ptr += hostname[ 0 ];
9582 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
9583 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
9584
9585 check( (size_t)( lim - ptr ) >= kLocalNameLen );
9586 memcpy( ptr, kLocalName, kLocalNameLen );
9587 ptr += kLocalNameLen;
9588
9589 rdataLen = (size_t)( ptr - rdataPtr );
9590
9591 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9592 (uint16_t) rdataLen, rdataPtr, &answer );
9593 require_noerr( err, exit );
9594 recordName = NULL;
9595 rdataPtr = NULL;
9596
9597 *answerPtr = answer;
9598 answerPtr = &answer->next;
9599 }
9600
9601 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9602 {
9603 err = DomainNameDupLower( inName, &recordName, NULL );
9604 require_noerr( err, exit );
9605
9606 rdataLen = txtSize;
9607 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
9608 require_noerr( err, exit );
9609
9610 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9611 (uint16_t) rdataLen, rdataPtr, &answer );
9612 require_noerr( err, exit );
9613 recordName = NULL;
9614 rdataPtr = NULL;
9615
9616 *answerPtr = answer;
9617 }
9618 else if( inType == kDNSServiceType_NSEC )
9619 {
9620 err = DomainNameDupLower( inName, &recordName, NULL );
9621 require_noerr( err, exit );
9622
9623 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
9624 require_noerr( err, exit );
9625
9626 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9627 (uint16_t) rdataLen, rdataPtr, &answer );
9628 require_noerr( err, exit );
9629 recordName = NULL;
9630 rdataPtr = NULL;
9631
9632 *answerPtr = answer;
9633 }
9634 }
9635 err = kNoErr;
9636
9637 exit:
9638 FreeNullSafe( recordName );
9639 FreeNullSafe( rdataPtr );
9640 return( err );
9641 }
9642
9643 //===========================================================================================================================
9644 // _MDNSReplierAnswerListRemovePTR
9645 //===========================================================================================================================
9646
9647 static void
9648 _MDNSReplierAnswerListRemovePTR(
9649 MRResourceRecord ** inAnswerListPtr,
9650 const uint8_t * inName,
9651 const uint8_t * inRData )
9652 {
9653 MRResourceRecord * answer;
9654 MRResourceRecord ** answerPtr;
9655
9656 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9657 {
9658 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
9659 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
9660 }
9661 if( answer )
9662 {
9663 *answerPtr = answer->next;
9664 _MRResourceRecordFree( answer );
9665 }
9666 }
9667
9668 //===========================================================================================================================
9669 // _MDNSReplierSendOrDropResponse
9670 //===========================================================================================================================
9671
9672 static OSStatus
9673 _MDNSReplierSendOrDropResponse(
9674 MDNSReplierContext * inContext,
9675 MRResourceRecord * inAnswerList,
9676 sockaddr_ip * inQuerier,
9677 SocketRef inSock,
9678 unsigned int inIndex,
9679 Boolean inUnicast )
9680 {
9681 OSStatus err;
9682 uint8_t * responsePtr = NULL;
9683 size_t responseLen;
9684 const struct sockaddr * destAddr;
9685 ssize_t n;
9686 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
9687 int drop;
9688
9689 check( inIndex <= inContext->maxInstanceCount );
9690
9691 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9692 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9693 // as opposed to those for service instance records, are never dropped.
9694
9695 drop = false;
9696 if( inIndex > 0 )
9697 {
9698 if( inContext->maxDropCount > 0 )
9699 {
9700 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
9701
9702 if( *dropCount < inContext->maxDropCount )
9703 {
9704 if( ShouldDrop( dropRate ) ) drop = true;
9705 *dropCount += 1;
9706 }
9707 }
9708 else if( ShouldDrop( dropRate ) )
9709 {
9710 drop = true;
9711 }
9712 }
9713
9714 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
9715 require_noerr( err, exit );
9716
9717 if( inUnicast )
9718 {
9719 destAddr = &inQuerier->sa;
9720 }
9721 else
9722 {
9723 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9724 }
9725
9726 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9727 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
9728
9729 if( !drop )
9730 {
9731 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
9732 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
9733 require_noerr( err, exit );
9734 }
9735
9736 exit:
9737 FreeNullSafe( responsePtr );
9738 return( err );
9739 }
9740
9741 //===========================================================================================================================
9742 // _MDNSReplierCreateResponse
9743 //===========================================================================================================================
9744
9745 static OSStatus
9746 _MDNSReplierCreateResponse(
9747 MDNSReplierContext * inContext,
9748 MRResourceRecord * inAnswerList,
9749 unsigned int inIndex,
9750 uint8_t ** outResponsePtr,
9751 size_t * outResponseLen )
9752 {
9753 OSStatus err;
9754 DataBuffer responseDB;
9755 DNSHeader hdr;
9756 MRResourceRecord * answer;
9757 uint8_t * responsePtr;
9758 size_t responseLen, len;
9759 unsigned int answerCount, recordCount;
9760 MRNameOffsetItem * nameOffsetList = NULL;
9761
9762 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
9763
9764 // The current answers in the answer list will make up the response's Answer Record Section.
9765
9766 answerCount = 0;
9767 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
9768
9769 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9770
9771 if( !inContext->noAdditionals )
9772 {
9773 for( answer = inAnswerList; answer; answer = answer->next )
9774 {
9775 switch( answer->type )
9776 {
9777 case kDNSServiceType_PTR:
9778 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
9779 answer->class );
9780 require_noerr( err, exit );
9781
9782 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
9783 answer->class );
9784 require_noerr( err, exit );
9785 break;
9786
9787 case kDNSServiceType_SRV:
9788 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
9789 answer->class );
9790 require_noerr( err, exit );
9791
9792 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, 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_TXT:
9802 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9803 answer->class );
9804 require_noerr( err, exit );
9805 break;
9806
9807 case kDNSServiceType_A:
9808 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
9809 answer->class );
9810 require_noerr( err, exit );
9811
9812 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9813 answer->class );
9814 require_noerr( err, exit );
9815 break;
9816
9817 case kDNSServiceType_AAAA:
9818 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
9819 answer->class );
9820 require_noerr( err, exit );
9821
9822 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9823 answer->class );
9824 require_noerr( err, exit );
9825 break;
9826
9827 default:
9828 break;
9829 }
9830 }
9831 }
9832
9833 // Append a provisional header to the response message.
9834
9835 memset( &hdr, 0, sizeof( hdr ) );
9836 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
9837
9838 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
9839 require_noerr( err, exit );
9840
9841 // Append answers to response message.
9842
9843 responseLen = DataBuffer_GetLen( &responseDB );
9844 recordCount = 0;
9845 for( answer = inAnswerList; answer; answer = answer->next )
9846 {
9847 dns_fixed_fields_record fields;
9848 unsigned int class;
9849
9850 // Append record NAME.
9851
9852 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
9853 require_noerr( err, exit );
9854
9855 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9856
9857 class = answer->class;
9858 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
9859 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
9860 ( answer->type == kDNSServiceType_NSEC ) )
9861 {
9862 class |= kRRClassCacheFlushBit;
9863 }
9864
9865 dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
9866 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
9867 require_noerr( err, exit );
9868
9869 // Append record RDATA.
9870 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9871
9872 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
9873 ( answer->type == kDNSServiceType_NSEC ) )
9874 {
9875 size_t rdlength;
9876 uint8_t * rdLengthPtr;
9877 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
9878 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
9879
9880 if( answer->type == kDNSServiceType_PTR )
9881 {
9882 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9883 require_noerr( err, exit );
9884 }
9885 else if( answer->type == kDNSServiceType_SRV )
9886 {
9887 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
9888
9889 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
9890 require_noerr( err, exit );
9891
9892 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
9893 require_noerr( err, exit );
9894 }
9895 else
9896 {
9897 const size_t nameLen = DomainNameLength( answer->rdata );
9898
9899 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9900 require_noerr( err, exit );
9901
9902 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
9903
9904 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
9905 require_noerr( err, exit );
9906 }
9907
9908 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9909
9910 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
9911 check( rdlength <= UINT16_MAX );
9912
9913 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
9914 WriteBig16( rdLengthPtr, rdlength );
9915 }
9916 else
9917 {
9918 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
9919 require_noerr( err, exit );
9920 }
9921
9922 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
9923 responseLen = DataBuffer_GetLen( &responseDB );
9924 ++recordCount;
9925 }
9926
9927 // Set the response header's Answer and Additional record counts.
9928 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9929 // response message to exceed the maximum mDNS message size.
9930
9931 if( recordCount <= answerCount )
9932 {
9933 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
9934 }
9935 else
9936 {
9937 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
9938 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
9939 }
9940
9941 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
9942 require_noerr( err, exit );
9943
9944 if( outResponsePtr ) *outResponsePtr = responsePtr;
9945 if( outResponseLen ) *outResponseLen = responseLen;
9946
9947 exit:
9948 _MRNameOffsetItemFreeList( nameOffsetList );
9949 DataBuffer_Free( &responseDB );
9950 return( err );
9951 }
9952
9953 //===========================================================================================================================
9954 // _MDNSReplierAppendNameToResponse
9955 //===========================================================================================================================
9956
9957 static OSStatus
9958 _MDNSReplierAppendNameToResponse(
9959 DataBuffer * inResponse,
9960 const uint8_t * inName,
9961 MRNameOffsetItem ** inNameOffsetListPtr )
9962 {
9963 OSStatus err;
9964 const uint8_t * subname;
9965 const uint8_t * limit;
9966 size_t nameOffset;
9967 MRNameOffsetItem * item;
9968 uint8_t compressionPtr[ 2 ];
9969
9970 nameOffset = DataBuffer_GetLen( inResponse );
9971
9972 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9973
9974 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
9975 {
9976 for( item = *inNameOffsetListPtr; item; item = item->next )
9977 {
9978 if( DomainNameEqual( item->name, subname ) ) break;
9979 }
9980
9981 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9982 // the subname's first label.
9983
9984 if( item )
9985 {
9986 DNSMessageWriteLabelPointer( compressionPtr, item->offset );
9987
9988 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
9989 require_noerr( err, exit );
9990 break;
9991 }
9992 else
9993 {
9994 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
9995 require_noerr( err, exit );
9996 }
9997 }
9998
9999 // 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
10000 // label were appended to the response message, so a root label is needed to terminate the complete name.
10001
10002 if( subname[ 0 ] == 0 )
10003 {
10004 err = DataBuffer_Append( inResponse, "", 1 );
10005 require_noerr( err, exit );
10006 }
10007
10008 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
10009
10010 limit = subname;
10011 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
10012 {
10013 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
10014
10015 if( subnameOffset > kDNSCompressionOffsetMax ) break;
10016
10017 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
10018 require_noerr( err, exit );
10019
10020 item->next = *inNameOffsetListPtr;
10021 *inNameOffsetListPtr = item;
10022 }
10023 err = kNoErr;
10024
10025 exit:
10026 return( err );
10027 }
10028
10029 //===========================================================================================================================
10030 // _MDNSReplierServiceTypeMatch
10031 //===========================================================================================================================
10032
10033 static Boolean
10034 _MDNSReplierServiceTypeMatch(
10035 const MDNSReplierContext * inContext,
10036 const uint8_t * inName,
10037 unsigned int * outTXTSize,
10038 unsigned int * outCount )
10039 {
10040 OSStatus err;
10041 const char * ptr;
10042 const char * end;
10043 uint32_t txtSize, count;
10044 const uint8_t * const serviceLabel = inContext->serviceLabel;
10045 int nameMatches = false;
10046
10047 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
10048 if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
10049
10050 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
10051 end = (const char *) &inName[ 1 + inName[ 0 ] ];
10052
10053 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10054 ++ptr;
10055
10056 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
10057 require_noerr_quiet( err, exit );
10058 require_quiet( txtSize <= UINT16_MAX, exit );
10059
10060 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10061 ++ptr;
10062
10063 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
10064 require_noerr_quiet( err, exit );
10065 require_quiet( count <= UINT16_MAX, exit );
10066 require_quiet( ptr == end, exit );
10067
10068 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
10069 nameMatches = true;
10070
10071 if( outTXTSize ) *outTXTSize = txtSize;
10072 if( outCount ) *outCount = count;
10073
10074 exit:
10075 return( nameMatches ? true : false );
10076 }
10077
10078 //===========================================================================================================================
10079 // _MDNSReplierServiceInstanceNameMatch
10080 //===========================================================================================================================
10081
10082 static Boolean
10083 _MDNSReplierServiceInstanceNameMatch(
10084 const MDNSReplierContext * inContext,
10085 const uint8_t * inName,
10086 unsigned int * outIndex,
10087 unsigned int * outTXTSize,
10088 unsigned int * outCount )
10089 {
10090 OSStatus err;
10091 const uint8_t * ptr;
10092 const uint8_t * end;
10093 uint32_t index;
10094 unsigned int txtSize, count;
10095 const uint8_t * const hostname = inContext->hostname;
10096 int nameMatches = false;
10097
10098 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10099 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10100
10101 ptr = &inName[ 1 + hostname[ 0 ] ];
10102 end = &inName[ 1 + inName[ 0 ] ];
10103 if( ptr < end )
10104 {
10105 require_quiet( ( end - ptr ) >= 2, exit );
10106 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
10107 ptr += 2;
10108
10109 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10110 require_noerr_quiet( err, exit );
10111 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10112
10113 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
10114 ++ptr;
10115 }
10116 else
10117 {
10118 index = 1;
10119 }
10120
10121 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
10122 nameMatches = true;
10123
10124 if( outIndex ) *outIndex = index;
10125 if( outTXTSize ) *outTXTSize = txtSize;
10126 if( outCount ) *outCount = count;
10127
10128 exit:
10129 return( nameMatches ? true : false );
10130 }
10131
10132 //===========================================================================================================================
10133 // _MDNSReplierAboutRecordNameMatch
10134 //===========================================================================================================================
10135
10136 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10137 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10138
10139 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
10140 {
10141 const uint8_t * subname;
10142 const uint8_t * const hostname = inContext->hostname;
10143 int nameMatches = false;
10144
10145 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
10146 subname = DomainNameGetNextLabel( inName );
10147
10148 if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
10149 subname = DomainNameGetNextLabel( subname );
10150
10151 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
10152 nameMatches = true;
10153
10154 exit:
10155 return( nameMatches ? true : false );
10156 }
10157
10158 //===========================================================================================================================
10159 // _MDNSReplierHostnameMatch
10160 //===========================================================================================================================
10161
10162 static Boolean
10163 _MDNSReplierHostnameMatch(
10164 const MDNSReplierContext * inContext,
10165 const uint8_t * inName,
10166 unsigned int * outIndex )
10167 {
10168 OSStatus err;
10169 const uint8_t * ptr;
10170 const uint8_t * end;
10171 uint32_t index;
10172 const uint8_t * const hostname = inContext->hostname;
10173 int nameMatches = false;
10174
10175 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10176 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10177
10178 ptr = &inName[ 1 + hostname[ 0 ] ];
10179 end = &inName[ 1 + inName[ 0 ] ];
10180 if( ptr < end )
10181 {
10182 require_quiet( *ptr == '-', exit );
10183 ++ptr;
10184
10185 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10186 require_noerr_quiet( err, exit );
10187 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10188 require_quiet( ptr == end, exit );
10189 }
10190 else
10191 {
10192 index = 1;
10193 }
10194
10195 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
10196 nameMatches = true;
10197
10198 if( outIndex ) *outIndex = index;
10199
10200 exit:
10201 return( nameMatches ? true : false );
10202 }
10203
10204 //===========================================================================================================================
10205 // _MDNSReplierCreateTXTRecord
10206 //===========================================================================================================================
10207
10208 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
10209 {
10210 OSStatus err;
10211 uint8_t * txt;
10212 uint8_t * ptr;
10213 size_t i, wholeCount, remCount;
10214 uint32_t hash;
10215 int n;
10216 uint8_t txtStr[ 16 ];
10217
10218 require_action_quiet( inSize > 0, exit, err = kSizeErr );
10219
10220 txt = (uint8_t *) malloc( inSize );
10221 require_action( txt, exit, err = kNoMemoryErr );
10222
10223 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
10224
10225 txtStr[ 0 ] = 15;
10226 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
10227 check( n == 15 );
10228
10229 ptr = txt;
10230 wholeCount = inSize / 16;
10231 for( i = 0; i < wholeCount; ++i )
10232 {
10233 memcpy( ptr, txtStr, 16 );
10234 ptr += 16;
10235 }
10236
10237 remCount = inSize % 16;
10238 if( remCount > 0 )
10239 {
10240 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
10241 memcpy( ptr, txtStr, remCount );
10242 ptr += remCount;
10243 }
10244 check( ptr == &txt[ inSize ] );
10245
10246 *outTXT = txt;
10247 err = kNoErr;
10248
10249 exit:
10250 return( err );
10251 }
10252
10253 //===========================================================================================================================
10254 // _MRResourceRecordCreate
10255 //===========================================================================================================================
10256
10257 static OSStatus
10258 _MRResourceRecordCreate(
10259 uint8_t * inName,
10260 uint16_t inType,
10261 uint16_t inClass,
10262 uint32_t inTTL,
10263 uint16_t inRDLength,
10264 uint8_t * inRData,
10265 MRResourceRecord ** outRecord )
10266 {
10267 OSStatus err;
10268 MRResourceRecord * obj;
10269
10270 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
10271 require_action( obj, exit, err = kNoMemoryErr );
10272
10273 obj->name = inName;
10274 obj->type = inType;
10275 obj->class = inClass;
10276 obj->ttl = inTTL;
10277 obj->rdlength = inRDLength;
10278 obj->rdata = inRData;
10279
10280 if( inType == kDNSServiceType_SRV )
10281 {
10282 require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
10283 obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
10284 }
10285
10286 *outRecord = obj;
10287 obj = NULL;
10288 err = kNoErr;
10289
10290 exit:
10291 FreeNullSafe( obj );
10292 return( err );
10293 }
10294
10295 //===========================================================================================================================
10296 // _MRResourceRecordFree
10297 //===========================================================================================================================
10298
10299 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
10300 {
10301 ForgetMem( &inRecord->name );
10302 ForgetMem( &inRecord->rdata );
10303 free( inRecord );
10304 }
10305
10306 //===========================================================================================================================
10307 // _MRResourceRecordFreeList
10308 //===========================================================================================================================
10309
10310 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
10311 {
10312 MRResourceRecord * record;
10313
10314 while( ( record = inList ) != NULL )
10315 {
10316 inList = record->next;
10317 _MRResourceRecordFree( record );
10318 }
10319 }
10320
10321 //===========================================================================================================================
10322 // _MRNameOffsetItemCreate
10323 //===========================================================================================================================
10324
10325 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
10326 {
10327 OSStatus err;
10328 MRNameOffsetItem * obj;
10329 size_t nameLen;
10330
10331 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
10332
10333 nameLen = DomainNameLength( inName );
10334 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
10335 require_action( obj, exit, err = kNoMemoryErr );
10336
10337 obj->offset = inOffset;
10338 memcpy( obj->name, inName, nameLen );
10339
10340 *outItem = obj;
10341 err = kNoErr;
10342
10343 exit:
10344 return( err );
10345 }
10346
10347 //===========================================================================================================================
10348 // _MRNameOffsetItemFree
10349 //===========================================================================================================================
10350
10351 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
10352 {
10353 free( inItem );
10354 }
10355
10356 //===========================================================================================================================
10357 // _MRNameOffsetItemFreeList
10358 //===========================================================================================================================
10359
10360 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
10361 {
10362 MRNameOffsetItem * item;
10363
10364 while( ( item = inList ) != NULL )
10365 {
10366 inList = item->next;
10367 _MRNameOffsetItemFree( item );
10368 }
10369 }
10370
10371 //===========================================================================================================================
10372 // GAIPerfCmd
10373 //===========================================================================================================================
10374
10375 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10376
10377 typedef struct GAITesterPrivate * GAITesterRef;
10378 typedef struct GAITestCase GAITestCase;
10379
10380 typedef struct
10381 {
10382 const char * name; // Domain name that was resolved.
10383 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
10384 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
10385 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
10386 OSStatus error;
10387
10388 } GAITestItemResult;
10389
10390 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
10391 typedef void
10392 ( *GAITesterResultsHandler_f )(
10393 const char * inCaseTitle,
10394 NanoTime64 inCaseStartTime,
10395 NanoTime64 inCaseEndTime,
10396 const GAITestItemResult * inResultArray,
10397 size_t inResultCount,
10398 void * inContext );
10399
10400 typedef unsigned int GAITestAddrType;
10401 #define kGAITestAddrType_None 0
10402 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10403 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10404 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10405
10406 #define GAITestAddrTypeIsValid( X ) \
10407 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10408
10409 typedef struct
10410 {
10411 GAITesterRef tester; // GAI tester object.
10412 CFMutableArrayRef testCaseResults; // Array of test case results.
10413 unsigned int iterTimeLimitMs; // Amount of time to allow each iteration to complete.
10414 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10415 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
10416 unsigned int defaultIterCount; // Default test case iteration count.
10417 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
10418 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
10419 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
10420 OutputFormatType outputFormat; // Format of test results output.
10421 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10422 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
10423 Boolean testFailed; // True if at least one test case iteration failed.
10424
10425 } GAIPerfContext;
10426
10427 static void GAIPerfContextFree( GAIPerfContext *inContext );
10428 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
10429 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
10430 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
10431 static void
10432 GAIPerfResultsHandler(
10433 const char * inCaseTitle,
10434 NanoTime64 inCaseStartTime,
10435 NanoTime64 inCaseEndTime,
10436 const GAITestItemResult * inResultArray,
10437 size_t inResultCount,
10438 void * inContext );
10439 static void GAIPerfSignalHandler( void *inContext );
10440
10441 CFTypeID GAITesterGetTypeID( void );
10442 static OSStatus
10443 GAITesterCreate(
10444 dispatch_queue_t inQueue,
10445 unsigned int inCallDelayMs,
10446 int inServerDelayMs,
10447 int inServerDefaultTTL,
10448 Boolean inSkipPathEvaluation,
10449 Boolean inBadUDPMode,
10450 GAITesterRef * outTester );
10451 static void GAITesterStart( GAITesterRef inTester );
10452 static void GAITesterStop( GAITesterRef inTester );
10453 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
10454 static void
10455 GAITesterSetStopHandler(
10456 GAITesterRef inTester,
10457 GAITesterStopHandler_f inEventHandler,
10458 void * inEventContext );
10459 static void
10460 GAITesterSetResultsHandler(
10461 GAITesterRef inTester,
10462 GAITesterResultsHandler_f inResultsHandler,
10463 void * inResultsContext );
10464
10465 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
10466 static void GAITestCaseFree( GAITestCase *inCase );
10467 static OSStatus
10468 GAITestCaseAddItem(
10469 GAITestCase * inCase,
10470 unsigned int inAliasCount,
10471 unsigned int inAddressCount,
10472 int inTTL,
10473 GAITestAddrType inHasAddrs,
10474 GAITestAddrType inWantAddrs,
10475 unsigned int inTimeLimitMs,
10476 unsigned int inItemCount );
10477 static OSStatus
10478 GAITestCaseAddLocalHostItem(
10479 GAITestCase * inCase,
10480 GAITestAddrType inWantAddrs,
10481 unsigned int inTimeLimitMs,
10482 unsigned int inItemCount );
10483
10484 static void GAIPerfCmd( void )
10485 {
10486 OSStatus err;
10487 GAIPerfContext * context = NULL;
10488
10489 err = CheckRootUser();
10490 require_noerr_quiet( err, exit );
10491
10492 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
10493 require_noerr_quiet( err, exit );
10494
10495 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
10496 require_noerr_quiet( err, exit );
10497
10498 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
10499 require_noerr_quiet( err, exit );
10500
10501 err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
10502 require_noerr_quiet( err, exit );
10503
10504 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
10505 require_action( context, exit, err = kNoMemoryErr );
10506
10507 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
10508 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
10509
10510 context->iterTimeLimitMs = (unsigned int) gGAIPerf_IterationTimeLimitMs;
10511 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
10512 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
10513 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
10514 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
10515 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
10516
10517 if( gGAIPerf_OutputFilePath )
10518 {
10519 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
10520 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
10521 }
10522
10523 err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
10524 require_noerr_quiet( err, exit );
10525
10526 err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
10527 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
10528 require_noerr( err, exit );
10529
10530 check( gGAIPerf_TestSuite );
10531 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
10532 {
10533 err = GAIPerfAddBasicTestCases( context );
10534 require_noerr( err, exit );
10535 }
10536 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
10537 {
10538 err = GAIPerfAddAdvancedTestCases( context );
10539 require_noerr( err, exit );
10540 }
10541 else
10542 {
10543 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
10544 goto exit;
10545 }
10546
10547 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
10548 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
10549
10550 signal( SIGINT, SIG_IGN );
10551 err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource );
10552 require_noerr( err, exit );
10553 dispatch_resume( context->sigIntSource );
10554
10555 signal( SIGTERM, SIG_IGN );
10556 err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource );
10557 require_noerr( err, exit );
10558 dispatch_resume( context->sigTermSource );
10559
10560 GAITesterStart( context->tester );
10561 dispatch_main();
10562
10563 exit:
10564 if( context ) GAIPerfContextFree( context );
10565 exit( 1 );
10566 }
10567
10568 //===========================================================================================================================
10569 // GAIPerfContextFree
10570 //===========================================================================================================================
10571
10572 static void GAIPerfContextFree( GAIPerfContext *inContext )
10573 {
10574 ForgetCF( &inContext->tester );
10575 ForgetCF( &inContext->testCaseResults );
10576 ForgetMem( &inContext->outputFilePath );
10577 dispatch_source_forget( &inContext->sigIntSource );
10578 dispatch_source_forget( &inContext->sigTermSource );
10579 free( inContext );
10580 }
10581
10582 //===========================================================================================================================
10583 // GAIPerfAddAdvancedTestCases
10584 //===========================================================================================================================
10585
10586 #define kTestCaseTitleBufferSize 128
10587
10588 static void
10589 _GAIPerfWriteTestCaseTitle(
10590 char inBuffer[ kTestCaseTitleBufferSize ],
10591 unsigned int inCNAMERecordCount,
10592 unsigned int inARecordCount,
10593 unsigned int inAAAARecordCount,
10594 GAITestAddrType inRequested,
10595 unsigned int inIterationCount,
10596 Boolean inIterationsAreUnique );
10597 static void
10598 _GAIPerfWriteLocalHostTestCaseTitle(
10599 char inBuffer[ kTestCaseTitleBufferSize ],
10600 GAITestAddrType inRequested,
10601 unsigned int inIterationCount );
10602
10603 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10604 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10605
10606 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
10607 {
10608 OSStatus err;
10609 unsigned int aliasCount, addressCount, i;
10610 GAITestCase * testCase = NULL;
10611 char title[ kTestCaseTitleBufferSize ];
10612
10613 aliasCount = 0;
10614 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
10615 {
10616 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
10617 {
10618 // Add a test case to resolve a domain name with
10619 //
10620 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10621 //
10622 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10623 // requires server queries.
10624
10625 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10626 inContext->defaultIterCount, true );
10627
10628 err = GAITestCaseCreate( title, &testCase );
10629 require_noerr( err, exit );
10630
10631 for( i = 0; i < inContext->defaultIterCount; ++i )
10632 {
10633 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10634 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10635 require_noerr( err, exit );
10636 }
10637
10638 err = GAITesterAddTestCase( inContext->tester, testCase );
10639 require_noerr( err, exit );
10640 testCase = NULL;
10641
10642 // Add a test case to resolve a domain name with
10643 //
10644 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10645 //
10646 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10647 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10648 // ideally require no server queries, i.e., the results should come from the cache.
10649
10650 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10651 inContext->defaultIterCount, false );
10652
10653 err = GAITestCaseCreate( title, &testCase );
10654 require_noerr( err, exit );
10655
10656 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10657 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
10658 require_noerr( err, exit );
10659
10660 err = GAITesterAddTestCase( inContext->tester, testCase );
10661 require_noerr( err, exit );
10662 testCase = NULL;
10663 }
10664
10665 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
10666 }
10667
10668 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10669
10670 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10671
10672 err = GAITestCaseCreate( title, &testCase );
10673 require_noerr( err, exit );
10674
10675 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10676 inContext->defaultIterCount );
10677 require_noerr( err, exit );
10678
10679 err = GAITesterAddTestCase( inContext->tester, testCase );
10680 require_noerr( err, exit );
10681 testCase = NULL;
10682
10683 exit:
10684 if( testCase ) GAITestCaseFree( testCase );
10685 return( err );
10686 }
10687
10688 //===========================================================================================================================
10689 // _GAIPerfWriteTestCaseTitle
10690 //===========================================================================================================================
10691
10692 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10693 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10694 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10695 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10696 "" )
10697
10698 static void
10699 _GAIPerfWriteTestCaseTitle(
10700 char inBuffer[ kTestCaseTitleBufferSize ],
10701 unsigned int inCNAMERecordCount,
10702 unsigned int inARecordCount,
10703 unsigned int inAAAARecordCount,
10704 GAITestAddrType inRequested,
10705 unsigned int inIterationCount,
10706 Boolean inIterationsAreUnique )
10707 {
10708 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10709 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
10710 inIterationCount, inIterationsAreUnique, ",unique" );
10711 }
10712
10713 //===========================================================================================================================
10714 // _GAIPerfWriteLocalHostTestCaseTitle
10715 //===========================================================================================================================
10716
10717 static void
10718 _GAIPerfWriteLocalHostTestCaseTitle(
10719 char inBuffer[ kTestCaseTitleBufferSize ],
10720 GAITestAddrType inRequested,
10721 unsigned int inIterationCount )
10722 {
10723 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
10724 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
10725 }
10726
10727 //===========================================================================================================================
10728 // GAIPerfAddBasicTestCases
10729 //===========================================================================================================================
10730
10731 #define kGAIPerfBasicTestSuite_AliasCount 2
10732 #define kGAIPerfBasicTestSuite_AddrCount 4
10733
10734 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
10735 {
10736 OSStatus err;
10737 GAITestCase * testCase = NULL;
10738 char title[ kTestCaseTitleBufferSize ];
10739 unsigned int i;
10740
10741 // Test Case #1:
10742 // Resolve a domain name with
10743 //
10744 // 2 CNAME records, 4 A records, and 4 AAAA records
10745 //
10746 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10747 // queries.
10748
10749 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10750 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10751 inContext->defaultIterCount, true );
10752
10753 err = GAITestCaseCreate( title, &testCase );
10754 require_noerr( err, exit );
10755
10756 for( i = 0; i < inContext->defaultIterCount; ++i )
10757 {
10758 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10759 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
10760 require_noerr( err, exit );
10761 }
10762
10763 err = GAITesterAddTestCase( inContext->tester, testCase );
10764 require_noerr( err, exit );
10765 testCase = NULL;
10766
10767 // Test Case #2:
10768 // Resolve a domain name with
10769 //
10770 // 2 CNAME records, 4 A records, and 4 AAAA records
10771 //
10772 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10773 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10774 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10775
10776 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10777 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10778 inContext->defaultIterCount, false );
10779
10780 err = GAITestCaseCreate( title, &testCase );
10781 require_noerr( err, exit );
10782
10783 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10784 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10785 inContext->defaultIterCount + 1 );
10786 require_noerr( err, exit );
10787
10788 err = GAITesterAddTestCase( inContext->tester, testCase );
10789 require_noerr( err, exit );
10790 testCase = NULL;
10791
10792 // Test Case #3:
10793 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10794
10795 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10796
10797 err = GAITestCaseCreate( title, &testCase );
10798 require_noerr( err, exit );
10799
10800 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
10801 inContext->defaultIterCount );
10802 require_noerr( err, exit );
10803
10804 err = GAITesterAddTestCase( inContext->tester, testCase );
10805 require_noerr( err, exit );
10806 testCase = NULL;
10807
10808 exit:
10809 if( testCase ) GAITestCaseFree( testCase );
10810 return( err );
10811 }
10812
10813 //===========================================================================================================================
10814 // GAIPerfTesterStopHandler
10815 //===========================================================================================================================
10816
10817 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10818 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10819 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10820
10821 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10822 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10823 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10824 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10825
10826 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
10827 {
10828 OSStatus err;
10829 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10830 CFPropertyListRef plist;
10831 int exitCode;
10832
10833 err = inError;
10834 require_noerr_quiet( err, exit );
10835
10836 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
10837 "{"
10838 "%kO=" // info
10839 "{"
10840 "%kO=%lli" // callDelayMs
10841 "%kO=%lli" // serverDelayMs
10842 "%kO=%b" // skippedPathEval
10843 "%kO=%b" // usedBadUPDMode
10844 "}"
10845 "%kO=%O" // testCases
10846 "%kO=%b" // success
10847 "}",
10848 kGAIPerfResultsKey_Info,
10849 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
10850 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
10851 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
10852 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
10853 kGAIPerfResultsKey_TestCases, context->testCaseResults,
10854 kGAIPerfResultsKey_Success, !context->testFailed );
10855 require_noerr( err, exit );
10856
10857 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
10858 CFRelease( plist );
10859 require_noerr( err, exit );
10860
10861 exit:
10862 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
10863 GAIPerfContextFree( context );
10864 exit( exitCode );
10865 }
10866
10867 //===========================================================================================================================
10868 // GAIPerfResultsHandler
10869 //===========================================================================================================================
10870
10871 // Keys for test case dictionary
10872
10873 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10874 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10875 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10876 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10877 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10878 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10879 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10880
10881 // Keys for test case results array entry dictionaries
10882
10883 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10884 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10885 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10886 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10887
10888 // Keys for test case stats dictionaries
10889
10890 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10891 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10892 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10893 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10894 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10895
10896 typedef struct
10897 {
10898 double min;
10899 double max;
10900 double mean;
10901 double stdDev;
10902
10903 } GAIPerfStats;
10904
10905 #define GAIPerfStatsInit( X ) \
10906 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10907
10908 static void
10909 GAIPerfResultsHandler(
10910 const char * inCaseTitle,
10911 NanoTime64 inCaseStartTime,
10912 NanoTime64 inCaseEndTime,
10913 const GAITestItemResult * inResultArray,
10914 size_t inResultCount,
10915 void * inContext )
10916 {
10917 OSStatus err;
10918 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10919 int namesAreDynamic, namesAreUnique;
10920 const char * ptr;
10921 size_t count, startIndex;
10922 CFMutableArrayRef results = NULL;
10923 GAIPerfStats stats, firstStats, connStats;
10924 double sum, firstSum, connSum;
10925 size_t keyValueLen, i;
10926 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10927 char startTime[ 32 ];
10928 char endTime[ 32 ];
10929 const GAITestItemResult * result;
10930
10931 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10932 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10933 // the domain name's CNAME, A, and AAAA records.
10934
10935 namesAreDynamic = false;
10936 namesAreUnique = false;
10937 ptr = inCaseTitle;
10938 while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
10939 {
10940 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
10941 {
10942 namesAreDynamic = true;
10943 }
10944 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
10945 {
10946 namesAreUnique = true;
10947 }
10948 if( namesAreDynamic && namesAreUnique ) break;
10949 }
10950
10951 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
10952 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
10953 require_action( results, exit, err = kNoMemoryErr );
10954
10955 GAIPerfStatsInit( &stats );
10956 GAIPerfStatsInit( &firstStats );
10957 GAIPerfStatsInit( &connStats );
10958
10959 sum = 0.0;
10960 firstSum = 0.0;
10961 connSum = 0.0;
10962 count = 0;
10963 for( i = startIndex; i < inResultCount; ++i )
10964 {
10965 double value;
10966
10967 result = &inResultArray[ i ];
10968
10969 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
10970 "{"
10971 "%kO=%s" // name
10972 "%kO=%lli" // connectionTimeUs
10973 "%kO=%lli" // firstTimeUs
10974 "%kO=%lli" // timeUs
10975 "%kO=%lli" // error
10976 "}",
10977 kGAIPerfTestCaseResultKey_Name, result->name,
10978 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
10979 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
10980 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
10981 CFSTR( "error" ), (int64_t) result->error );
10982 require_noerr( err, exit );
10983
10984 if( !result->error )
10985 {
10986 value = (double) result->timeUs;
10987 if( value < stats.min ) stats.min = value;
10988 if( value > stats.max ) stats.max = value;
10989 sum += value;
10990
10991 value = (double) result->firstTimeUs;
10992 if( value < firstStats.min ) firstStats.min = value;
10993 if( value > firstStats.max ) firstStats.max = value;
10994 firstSum += value;
10995
10996 value = (double) result->connectionTimeUs;
10997 if( value < connStats.min ) connStats.min = value;
10998 if( value > connStats.max ) connStats.max = value;
10999 connSum += value;
11000
11001 ++count;
11002 }
11003 else
11004 {
11005 context->testFailed = true;
11006 }
11007 }
11008
11009 if( count > 0 )
11010 {
11011 stats.mean = sum / count;
11012 firstStats.mean = firstSum / count;
11013 connStats.mean = connSum / count;
11014
11015 sum = 0.0;
11016 firstSum = 0.0;
11017 connSum = 0.0;
11018 for( i = startIndex; i < inResultCount; ++i )
11019 {
11020 double diff;
11021
11022 result = &inResultArray[ i ];
11023 if( result->error ) continue;
11024
11025 diff = stats.mean - (double) result->timeUs;
11026 sum += ( diff * diff );
11027
11028 diff = firstStats.mean - (double) result->firstTimeUs;
11029 firstSum += ( diff * diff );
11030
11031 diff = connStats.mean - (double) result->connectionTimeUs;
11032 connSum += ( diff * diff );
11033 }
11034 stats.stdDev = sqrt( sum / count );
11035 firstStats.stdDev = sqrt( firstSum / count );
11036 connStats.stdDev = sqrt( connSum / count );
11037 }
11038
11039 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
11040 "{"
11041 "%kO=%s"
11042 "%kO=%s"
11043 "%kO=%s"
11044 "%kO=%O"
11045 "%kO="
11046 "{"
11047 "%kO=%lli"
11048 "%kO=%f"
11049 "%kO=%f"
11050 "%kO=%f"
11051 "%kO=%f"
11052 "}"
11053 "%kO="
11054 "{"
11055 "%kO=%lli"
11056 "%kO=%f"
11057 "%kO=%f"
11058 "%kO=%f"
11059 "%kO=%f"
11060 "}"
11061 "%kO="
11062 "{"
11063 "%kO=%lli"
11064 "%kO=%f"
11065 "%kO=%f"
11066 "%kO=%f"
11067 "%kO=%f"
11068 "}"
11069 "}",
11070 kGAIPerfTestCaseKey_Title, inCaseTitle,
11071 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
11072 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
11073 kGAIPerfTestCaseKey_Results, results,
11074 kGAIPerfTestCaseKey_Stats,
11075 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11076 kGAIPerfTestCaseStatsKey_Min, stats.min,
11077 kGAIPerfTestCaseStatsKey_Max, stats.max,
11078 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
11079 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
11080 kGAIPerfTestCaseKey_FirstStats,
11081 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11082 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
11083 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
11084 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
11085 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
11086 kGAIPerfTestCaseKey_ConnectionStats,
11087 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11088 kGAIPerfTestCaseStatsKey_Min, connStats.min,
11089 kGAIPerfTestCaseStatsKey_Max, connStats.max,
11090 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
11091 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
11092 require_noerr( err, exit );
11093
11094 exit:
11095 CFReleaseNullSafe( results );
11096 if( err ) exit( 1 );
11097 }
11098
11099 //===========================================================================================================================
11100 // GAIPerfSignalHandler
11101 //===========================================================================================================================
11102
11103 static void GAIPerfSignalHandler( void *inContext )
11104 {
11105 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11106
11107 if( !context->tester ) exit( 1 );
11108 GAITesterStop( context->tester );
11109 context->tester = NULL;
11110 }
11111
11112 //===========================================================================================================================
11113 // GAITesterCreate
11114 //===========================================================================================================================
11115
11116 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11117 // possible strings to use in the Tag label.
11118
11119 #define kGAITesterTagStringLen 6
11120
11121 typedef struct GAITestItem GAITestItem;
11122 struct GAITestItem
11123 {
11124 GAITestItem * next; // Next test item in list.
11125 char * name; // Domain name to resolve.
11126 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
11127 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
11128 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
11129 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
11130 OSStatus error; // Current status/error.
11131 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
11132 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
11133 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
11134 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11135 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11136 };
11137
11138 struct GAITestCase
11139 {
11140 GAITestCase * next; // Next test case in list.
11141 GAITestItem * itemList; // List of test items.
11142 char * title; // Title of the test case.
11143 };
11144
11145 struct GAITesterPrivate
11146 {
11147 CFRuntimeBase base; // CF object base.
11148 dispatch_queue_t queue; // Serial work queue.
11149 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
11150 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
11151 GAITestCase * caseList; // List of test cases.
11152 GAITestCase * currentCase; // Pointer to the current test case.
11153 GAITestItem * currentItem; // Pointer to the current test item.
11154 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
11155 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
11156 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11157 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11158 Boolean stopped; // True if the tester has been stopped.
11159 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
11160 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
11161 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
11162 pid_t serverPID; // PID of the test DNS server.
11163 int serverDelayMs; // Additional time to have the server delay its responses by.
11164 int serverDefaultTTL; // Default TTL for the server's records.
11165 GAITesterStopHandler_f stopHandler; // User's stop handler.
11166 void * stopContext; // User's event handler context.
11167 GAITesterResultsHandler_f resultsHandler; // User's results handler.
11168 void * resultsContext; // User's results handler context.
11169
11170 // Variables for current test item.
11171
11172 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
11173 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
11174 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
11175 uint64_t connTicks; // Ticks when the connection was created.
11176 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
11177 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
11178 Boolean gotFirstResult; // True if the first result has been received.
11179 };
11180
11181 CF_CLASS_DEFINE( GAITester );
11182
11183 static void _GAITesterStartNextTest( GAITesterRef inTester );
11184 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
11185 static void _GAITesterFirstGAITimeout( void *inContext );
11186 static void _GAITesterTimeout( void *inContext );
11187 static void DNSSD_API
11188 _GAITesterFirstGAICallback(
11189 DNSServiceRef inSDRef,
11190 DNSServiceFlags inFlags,
11191 uint32_t inInterfaceIndex,
11192 DNSServiceErrorType inError,
11193 const char * inHostname,
11194 const struct sockaddr * inSockAddr,
11195 uint32_t inTTL,
11196 void * inContext );
11197 static void DNSSD_API
11198 _GAITesterGetAddrInfoCallback(
11199 DNSServiceRef inSDRef,
11200 DNSServiceFlags inFlags,
11201 uint32_t inInterfaceIndex,
11202 DNSServiceErrorType inError,
11203 const char * inHostname,
11204 const struct sockaddr * inSockAddr,
11205 uint32_t inTTL,
11206 void * inContext );
11207 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
11208
11209 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11210
11211 static OSStatus
11212 GAITestItemCreate(
11213 const char * inName,
11214 unsigned int inAddressCount,
11215 GAITestAddrType inHasAddrs,
11216 GAITestAddrType inWantAddrs,
11217 unsigned int inTimeLimitMs,
11218 GAITestItem ** outItem );
11219 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
11220 static void GAITestItemFree( GAITestItem *inItem );
11221
11222 static OSStatus
11223 GAITesterCreate(
11224 dispatch_queue_t inQueue,
11225 unsigned int inCallDelayMs,
11226 int inServerDelayMs,
11227 int inServerDefaultTTL,
11228 Boolean inSkipPathEvaluation,
11229 Boolean inBadUDPMode,
11230 GAITesterRef * outTester )
11231 {
11232 OSStatus err;
11233 GAITesterRef obj = NULL;
11234
11235 CF_OBJECT_CREATE( GAITester, obj, err, exit );
11236
11237 ReplaceDispatchQueue( &obj->queue, inQueue );
11238 obj->callDelayMs = inCallDelayMs;
11239 obj->serverPID = -1;
11240 obj->serverDelayMs = inServerDelayMs;
11241 obj->serverDefaultTTL = inServerDefaultTTL;
11242 obj->skipPathEval = inSkipPathEvaluation;
11243 obj->badUDPMode = inBadUDPMode;
11244
11245 *outTester = obj;
11246 obj = NULL;
11247 err = kNoErr;
11248
11249 exit:
11250 CFReleaseNullSafe( obj );
11251 return( err );
11252 }
11253
11254 //===========================================================================================================================
11255 // _GAITesterFinalize
11256 //===========================================================================================================================
11257
11258 static void _GAITesterFinalize( CFTypeRef inObj )
11259 {
11260 GAITesterRef const me = (GAITesterRef) inObj;
11261 GAITestCase * testCase;
11262
11263 check( !me->getAddrInfo );
11264 check( !me->connection );
11265 check( !me->timer );
11266 dispatch_forget( &me->queue );
11267 while( ( testCase = me->caseList ) != NULL )
11268 {
11269 me->caseList = testCase->next;
11270 GAITestCaseFree( testCase );
11271 }
11272 }
11273
11274 //===========================================================================================================================
11275 // GAITesterStart
11276 //===========================================================================================================================
11277
11278 static void _GAITesterStart( void *inContext );
11279 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
11280
11281 static void GAITesterStart( GAITesterRef me )
11282 {
11283 CFRetain( me );
11284 dispatch_async_f( me->queue, me, _GAITesterStart );
11285 }
11286
11287 #define kGAITesterFirstGAITimeoutSecs 4
11288
11289 static void _GAITesterStart( void *inContext )
11290 {
11291 OSStatus err;
11292 GAITesterRef const me = (GAITesterRef) inContext;
11293 DNSServiceFlags flags;
11294 char name[ 64 ];
11295 char tag[ kGAITesterTagStringLen + 1 ];
11296
11297 err = SpawnCommand( &me->serverPID, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11298 (int64_t) getpid(),
11299 me->serverDefaultTTL >= 0, " --defaultTTL ",
11300 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
11301 me->serverDelayMs >= 0, " --responseDelay ",
11302 me->serverDelayMs >= 0, me->serverDelayMs,
11303 me->badUDPMode, " --badUDPMode" );
11304 require_noerr_quiet( err, exit );
11305
11306 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test",
11307 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
11308
11309 flags = 0;
11310 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11311
11312 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
11313 _GAITesterFirstGAICallback, me );
11314 require_noerr( err, exit );
11315
11316 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
11317 require_noerr( err, exit );
11318
11319 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
11320 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
11321 _GAITesterFirstGAITimeout, me, &me->timer );
11322 require_noerr( err, exit );
11323 dispatch_resume( me->timer );
11324
11325 exit:
11326 if( err ) _GAITesterStop( me, err );
11327 }
11328
11329 //===========================================================================================================================
11330 // GAITesterStop
11331 //===========================================================================================================================
11332
11333 static void _GAITesterUserStop( void *inContext );
11334
11335 static void GAITesterStop( GAITesterRef me )
11336 {
11337 CFRetain( me );
11338 dispatch_async_f( me->queue, me, _GAITesterUserStop );
11339 }
11340
11341 static void _GAITesterUserStop( void *inContext )
11342 {
11343 GAITesterRef const me = (GAITesterRef) inContext;
11344
11345 _GAITesterStop( me, kCanceledErr );
11346 CFRelease( me );
11347 }
11348
11349 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
11350 {
11351 OSStatus err;
11352
11353 ForgetPacketCapture( &me->pcap );
11354 dispatch_source_forget( &me->timer );
11355 DNSServiceForget( &me->getAddrInfo );
11356 DNSServiceForget( &me->connection );
11357 if( me->serverPID != -1 )
11358 {
11359 err = kill( me->serverPID, SIGTERM );
11360 err = map_global_noerr_errno( err );
11361 check_noerr( err );
11362 me->serverPID = -1;
11363 }
11364
11365 if( !me->stopped )
11366 {
11367 me->stopped = true;
11368 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
11369 CFRelease( me );
11370 }
11371 }
11372
11373 //===========================================================================================================================
11374 // GAITesterAddTestCase
11375 //===========================================================================================================================
11376
11377 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
11378 {
11379 OSStatus err;
11380 GAITestCase ** ptr;
11381
11382 require_action_quiet( inCase->itemList, exit, err = kCountErr );
11383
11384 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
11385 *ptr = inCase;
11386 err = kNoErr;
11387
11388 exit:
11389 return( err );
11390 }
11391
11392 //===========================================================================================================================
11393 // GAITesterSetStopHandler
11394 //===========================================================================================================================
11395
11396 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
11397 {
11398 me->stopHandler = inStopHandler;
11399 me->stopContext = inStopContext;
11400 }
11401
11402 //===========================================================================================================================
11403 // GAITesterSetResultsHandler
11404 //===========================================================================================================================
11405
11406 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
11407 {
11408 me->resultsHandler = inResultsHandler;
11409 me->resultsContext = inResultsContext;
11410 }
11411
11412 //===========================================================================================================================
11413 // _GAITesterStartNextTest
11414 //===========================================================================================================================
11415
11416 static void _GAITesterStartNextTest( GAITesterRef me )
11417 {
11418 OSStatus err;
11419 GAITestItem * item;
11420 DNSServiceFlags flags;
11421 DNSServiceProtocol protocols;
11422 int done = false;
11423
11424 if( me->currentItem ) me->currentItem = me->currentItem->next;
11425
11426 if( !me->currentItem )
11427 {
11428 if( me->currentCase )
11429 {
11430 // No more test items means that the current test case has completed.
11431
11432 me->caseEndTime = NanoTimeGetCurrent();
11433
11434 if( me->resultsHandler )
11435 {
11436 size_t resultCount, i;
11437 GAITestItemResult * resultArray;
11438
11439 resultCount = 0;
11440 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
11441 check( resultCount > 0 );
11442
11443 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
11444 require_action( resultArray, exit, err = kNoMemoryErr );
11445
11446 item = me->currentCase->itemList;
11447 for( i = 0; i < resultCount; ++i )
11448 {
11449 resultArray[ i ].name = item->name;
11450 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
11451 resultArray[ i ].firstTimeUs = item->firstTimeUs;
11452 resultArray[ i ].timeUs = item->timeUs;
11453 resultArray[ i ].error = item->error;
11454 item = item->next;
11455 }
11456 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
11457 me->resultsContext );
11458 ForgetMem( &resultArray );
11459 }
11460
11461 me->currentCase = me->currentCase->next;
11462 if( !me->currentCase )
11463 {
11464 done = true;
11465 err = kNoErr;
11466 goto exit;
11467 }
11468 }
11469 else
11470 {
11471 me->currentCase = me->caseList;
11472 }
11473 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
11474 me->currentItem = me->currentCase->itemList;
11475 }
11476
11477 item = me->currentItem;
11478 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
11479
11480 if( !item->wantV4 ) me->bitmapV4 = 0;
11481 else if( !item->hasV4 ) me->bitmapV4 = 1;
11482 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11483 else me->bitmapV4 = ~UINT64_C( 0 );
11484
11485 if( !item->wantV6 ) me->bitmapV6 = 0;
11486 else if( !item->hasV6 ) me->bitmapV6 = 1;
11487 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11488 else me->bitmapV6 = ~UINT64_C( 0 );
11489 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
11490 me->gotFirstResult = false;
11491
11492 // Perform preliminary tasks if this is the start of a new test case.
11493
11494 if( item == me->currentCase->itemList )
11495 {
11496 // Flush mDNSResponder's cache.
11497
11498 err = systemf( NULL, "killall -HUP mDNSResponder" );
11499 require_noerr( err, exit );
11500 sleep( 1 );
11501
11502 me->caseStartTime = NanoTimeGetCurrent();
11503 me->caseEndTime = kNanoTime_Invalid;
11504 }
11505
11506 // Start a packet capture.
11507
11508 check( !me->pcap );
11509 err = _GAITesterCreatePacketCapture( &me->pcap );
11510 require_noerr( err, exit );
11511
11512 // Start timer for test item's time limit.
11513
11514 check( !me->timer );
11515 if( item->timeLimitMs > 0 )
11516 {
11517 unsigned int timeLimitMs;
11518
11519 timeLimitMs = item->timeLimitMs;
11520 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
11521 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
11522
11523 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
11524 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
11525 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
11526 require_noerr( err, exit );
11527 dispatch_resume( me->timer );
11528 }
11529
11530 // Call DNSServiceGetAddrInfo().
11531
11532 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
11533
11534 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
11535 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11536
11537 protocols = 0;
11538 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
11539 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
11540
11541 me->startTicks = UpTicks();
11542
11543 check( !me->connection );
11544 err = DNSServiceCreateConnection( &me->connection );
11545 require_noerr( err, exit );
11546
11547 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
11548 require_noerr( err, exit );
11549
11550 me->connTicks = UpTicks();
11551
11552 check( !me->getAddrInfo );
11553 me->getAddrInfo = me->connection;
11554 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
11555 _GAITesterGetAddrInfoCallback, me );
11556 require_noerr( err, exit );
11557
11558 exit:
11559 if( err || done ) _GAITesterStop( me, err );
11560 }
11561
11562 //===========================================================================================================================
11563 // _GAITesterCreatePacketCapture
11564 //===========================================================================================================================
11565
11566 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
11567 {
11568 OSStatus err;
11569 pcap_t * pcap;
11570 struct bpf_program program;
11571 char errBuf[ PCAP_ERRBUF_SIZE ];
11572
11573 pcap = pcap_create( "lo0", errBuf );
11574 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
11575
11576 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
11577 require_noerr_action( err, exit, err = kUnknownErr );
11578
11579 err = pcap_set_snaplen( pcap, 512 );
11580 require_noerr_action( err, exit, err = kUnknownErr );
11581
11582 err = pcap_set_immediate_mode( pcap, 0 );
11583 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11584
11585 err = pcap_activate( pcap );
11586 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11587
11588 err = pcap_setdirection( pcap, PCAP_D_INOUT );
11589 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11590
11591 err = pcap_setnonblock( pcap, 1, errBuf );
11592 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
11593
11594 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
11595 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11596
11597 err = pcap_setfilter( pcap, &program );
11598 pcap_freecode( &program );
11599 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11600
11601 *outPCap = pcap;
11602 pcap = NULL;
11603
11604 exit:
11605 if( pcap ) pcap_close( pcap );
11606 return( err );
11607 }
11608
11609 //===========================================================================================================================
11610 // _GAITesterFirstGAITimeout
11611 //===========================================================================================================================
11612
11613 static void _GAITesterFirstGAITimeout( void *inContext )
11614 {
11615 GAITesterRef const me = (GAITesterRef) inContext;
11616
11617 _GAITesterStop( me, kNoResourcesErr );
11618 }
11619
11620 //===========================================================================================================================
11621 // _GAITesterTimeout
11622 //===========================================================================================================================
11623
11624 static void _GAITesterTimeout( void *inContext )
11625 {
11626 GAITesterRef const me = (GAITesterRef) inContext;
11627
11628 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
11629 }
11630
11631 //===========================================================================================================================
11632 // _GAITesterFirstGAICallback
11633 //===========================================================================================================================
11634
11635 static void DNSSD_API
11636 _GAITesterFirstGAICallback(
11637 DNSServiceRef inSDRef,
11638 DNSServiceFlags inFlags,
11639 uint32_t inInterfaceIndex,
11640 DNSServiceErrorType inError,
11641 const char * inHostname,
11642 const struct sockaddr * inSockAddr,
11643 uint32_t inTTL,
11644 void * inContext )
11645 {
11646 GAITesterRef const me = (GAITesterRef) inContext;
11647
11648 Unused( inSDRef );
11649 Unused( inInterfaceIndex );
11650 Unused( inHostname );
11651 Unused( inSockAddr );
11652 Unused( inTTL );
11653
11654 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
11655 {
11656 dispatch_source_forget( &me->timer );
11657 DNSServiceForget( &me->getAddrInfo );
11658
11659 _GAITesterStartNextTest( me );
11660 }
11661 }
11662
11663 //===========================================================================================================================
11664 // _GAITesterGetAddrInfoCallback
11665 //===========================================================================================================================
11666
11667 static void DNSSD_API
11668 _GAITesterGetAddrInfoCallback(
11669 DNSServiceRef inSDRef,
11670 DNSServiceFlags inFlags,
11671 uint32_t inInterfaceIndex,
11672 DNSServiceErrorType inError,
11673 const char * inHostname,
11674 const struct sockaddr * inSockAddr,
11675 uint32_t inTTL,
11676 void * inContext )
11677 {
11678 OSStatus err;
11679 GAITesterRef const me = (GAITesterRef) inContext;
11680 GAITestItem * const item = me->currentItem;
11681 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
11682 uint64_t nowTicks;
11683 uint64_t * bitmapPtr;
11684 uint64_t bitmask;
11685 int hasAddr;
11686
11687 Unused( inSDRef );
11688 Unused( inInterfaceIndex );
11689 Unused( inHostname );
11690 Unused( inTTL );
11691
11692 nowTicks = UpTicks();
11693
11694 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
11695
11696 // Check if we were expecting an IP address result of this type.
11697
11698 if( sip->sa.sa_family == AF_INET )
11699 {
11700 bitmapPtr = &me->bitmapV4;
11701 hasAddr = item->hasV4;
11702 }
11703 else if( sip->sa.sa_family == AF_INET6 )
11704 {
11705 bitmapPtr = &me->bitmapV6;
11706 hasAddr = item->hasV6;
11707 }
11708 else
11709 {
11710 err = kTypeErr;
11711 goto exit;
11712 }
11713
11714 bitmask = 0;
11715 if( hasAddr )
11716 {
11717 uint32_t addrOffset;
11718
11719 require_noerr_action_quiet( inError, exit, err = inError );
11720
11721 if( sip->sa.sa_family == AF_INET )
11722 {
11723 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
11724
11725 if( strcasecmp( item->name, "localhost." ) == 0 )
11726 {
11727 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
11728 }
11729 else
11730 {
11731 addrOffset = addrV4 - kDNSServerBaseAddrV4;
11732 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11733 {
11734 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11735 }
11736 }
11737 }
11738 else
11739 {
11740 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
11741
11742 if( strcasecmp( item->name, "localhost." ) == 0 )
11743 {
11744 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
11745 }
11746 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
11747 {
11748 addrOffset = addrV6[ 15 ];
11749 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11750 {
11751 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11752 }
11753 }
11754 }
11755 }
11756 else
11757 {
11758 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
11759 bitmask = 1;
11760 }
11761 require_action_quiet( bitmask != 0, exit, err = kValueErr );
11762 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
11763
11764 *bitmapPtr &= ~bitmask;
11765 if( !me->gotFirstResult )
11766 {
11767 me->firstTicks = nowTicks;
11768 me->gotFirstResult = true;
11769 }
11770 err = kNoErr;
11771
11772 exit:
11773 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
11774 {
11775 me->endTicks = nowTicks;
11776 _GAITesterCompleteCurrentTest( me, err );
11777 }
11778 }
11779
11780 //===========================================================================================================================
11781 // _GAITesterCompleteCurrentTest
11782 //===========================================================================================================================
11783
11784 static OSStatus
11785 _GAITesterGetDNSMessageFromPacket(
11786 const uint8_t * inPacketPtr,
11787 size_t inPacketLen,
11788 const uint8_t ** outMsgPtr,
11789 size_t * outMsgLen );
11790
11791 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
11792 {
11793 OSStatus err;
11794 GAITestItem * const item = me->currentItem;
11795 struct timeval timeStamps[ 4 ];
11796 struct timeval * tsPtr;
11797 struct timeval * tsQA = NULL;
11798 struct timeval * tsQAAAA = NULL;
11799 struct timeval * tsRA = NULL;
11800 struct timeval * tsRAAAA = NULL;
11801 struct timeval * t1;
11802 struct timeval * t2;
11803 int64_t idleTimeUs;
11804 uint8_t name[ kDomainNameLengthMax ];
11805
11806 dispatch_source_forget( &me->timer );
11807 DNSServiceForget( &me->getAddrInfo );
11808 DNSServiceForget( &me->connection );
11809
11810 item->error = inError;
11811 if( item->error )
11812 {
11813 err = kNoErr;
11814 goto exit;
11815 }
11816
11817 err = DomainNameFromString( name, item->name, NULL );
11818 require_noerr( err, exit );
11819
11820 tsPtr = &timeStamps[ 0 ];
11821 for( ;; )
11822 {
11823 int status;
11824 struct pcap_pkthdr * pktHdr;
11825 const uint8_t * packet;
11826 const uint8_t * msgPtr;
11827 size_t msgLen;
11828 const DNSHeader * hdr;
11829 unsigned int flags;
11830 const uint8_t * ptr;
11831 uint16_t qtype, qclass;
11832 uint8_t qname[ kDomainNameLengthMax ];
11833
11834 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
11835 if( status != 1 ) break;
11836 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
11837 if( msgLen < kDNSHeaderLength ) continue;
11838
11839 hdr = (const DNSHeader *) msgPtr;
11840 flags = DNSHeaderGetFlags( hdr );
11841 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
11842 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
11843
11844 ptr = (const uint8_t *) &hdr[ 1 ];
11845 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
11846 if( qclass != kDNSServiceClass_IN ) continue;
11847 if( !DomainNameEqual( qname, name ) ) continue;
11848
11849 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
11850 {
11851 if( flags & kDNSHeaderFlag_Response )
11852 {
11853 if( tsQA && !tsRA )
11854 {
11855 tsRA = tsPtr++;
11856 *tsRA = pktHdr->ts;
11857 }
11858 }
11859 else if( !tsQA )
11860 {
11861 tsQA = tsPtr++;
11862 *tsQA = pktHdr->ts;
11863 }
11864 }
11865 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
11866 {
11867 if( flags & kDNSHeaderFlag_Response )
11868 {
11869 if( tsQAAAA && !tsRAAAA )
11870 {
11871 tsRAAAA = tsPtr++;
11872 *tsRAAAA = pktHdr->ts;
11873 }
11874 }
11875 else if( !tsQAAAA )
11876 {
11877 tsQAAAA = tsPtr++;
11878 *tsQAAAA = pktHdr->ts;
11879 }
11880 }
11881 }
11882
11883 // t1 is the time when the last query was sent.
11884
11885 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
11886 else t1 = tsQA ? tsQA : tsQAAAA;
11887
11888 // t2 is when the first response was received.
11889
11890 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
11891 else t2 = tsRA ? tsRA : tsRAAAA;
11892
11893 if( t1 && t2 )
11894 {
11895 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
11896 if( idleTimeUs < 0 ) idleTimeUs = 0;
11897 }
11898 else
11899 {
11900 idleTimeUs = 0;
11901 }
11902
11903 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
11904 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11905 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11906
11907 exit:
11908 ForgetPacketCapture( &me->pcap );
11909 if( err ) _GAITesterStop( me, err );
11910 else _GAITesterStartNextTest( me );
11911 }
11912
11913 //===========================================================================================================================
11914 // _GAITesterGetDNSMessageFromPacket
11915 //===========================================================================================================================
11916
11917 #define kHeaderSizeNullLink 4
11918 #define kHeaderSizeIPv4Min 20
11919 #define kHeaderSizeIPv6 40
11920 #define kHeaderSizeUDP 8
11921
11922 #define kIPProtocolUDP 0x11
11923
11924 static OSStatus
11925 _GAITesterGetDNSMessageFromPacket(
11926 const uint8_t * inPacketPtr,
11927 size_t inPacketLen,
11928 const uint8_t ** outMsgPtr,
11929 size_t * outMsgLen )
11930 {
11931 OSStatus err;
11932 const uint8_t * nullLink;
11933 uint32_t addressFamily;
11934 const uint8_t * ip;
11935 int ipHeaderLen;
11936 int protocol;
11937 const uint8_t * msg;
11938 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
11939
11940 nullLink = &inPacketPtr[ 0 ];
11941 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
11942 addressFamily = ReadHost32( &nullLink[ 0 ] );
11943
11944 ip = &nullLink[ kHeaderSizeNullLink ];
11945 if( addressFamily == AF_INET )
11946 {
11947 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
11948 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
11949 protocol = ip[ 9 ];
11950 }
11951 else if( addressFamily == AF_INET6 )
11952 {
11953 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
11954 ipHeaderLen = kHeaderSizeIPv6;
11955 protocol = ip[ 6 ];
11956 }
11957 else
11958 {
11959 err = kTypeErr;
11960 goto exit;
11961 }
11962 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
11963 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
11964
11965 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
11966
11967 *outMsgPtr = msg;
11968 *outMsgLen = (size_t)( end - msg );
11969 err = kNoErr;
11970
11971 exit:
11972 return( err );
11973 }
11974
11975 //===========================================================================================================================
11976 // GAITestCaseCreate
11977 //===========================================================================================================================
11978
11979 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
11980 {
11981 OSStatus err;
11982 GAITestCase * obj;
11983
11984 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
11985 require_action( obj, exit, err = kNoMemoryErr );
11986
11987 obj->title = strdup( inTitle );
11988 require_action( obj->title, exit, err = kNoMemoryErr );
11989
11990 *outCase = obj;
11991 obj = NULL;
11992 err = kNoErr;
11993
11994 exit:
11995 if( obj ) GAITestCaseFree( obj );
11996 return( err );
11997 }
11998
11999 //===========================================================================================================================
12000 // GAITestCaseFree
12001 //===========================================================================================================================
12002
12003 static void GAITestCaseFree( GAITestCase *inCase )
12004 {
12005 GAITestItem * item;
12006
12007 while( ( item = inCase->itemList ) != NULL )
12008 {
12009 inCase->itemList = item->next;
12010 GAITestItemFree( item );
12011 }
12012 ForgetMem( &inCase->title );
12013 free( inCase );
12014 }
12015
12016 //===========================================================================================================================
12017 // GAITestCaseAddItem
12018 //===========================================================================================================================
12019
12020 static OSStatus
12021 GAITestCaseAddItem(
12022 GAITestCase * inCase,
12023 unsigned int inAliasCount,
12024 unsigned int inAddressCount,
12025 int inTTL,
12026 GAITestAddrType inHasAddrs,
12027 GAITestAddrType inWantAddrs,
12028 unsigned int inTimeLimitMs,
12029 unsigned int inItemCount )
12030 {
12031 OSStatus err;
12032 GAITestItem * item;
12033 GAITestItem * item2;
12034 GAITestItem * newItemList = NULL;
12035 GAITestItem ** itemPtr;
12036 char * ptr;
12037 char * end;
12038 unsigned int i;
12039 char name[ 64 ];
12040 char tag[ kGAITesterTagStringLen + 1 ];
12041
12042 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
12043
12044 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12045
12046 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
12047 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
12048 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12049
12050 ptr = &name[ 0 ];
12051 end = &name[ countof( name ) ];
12052
12053 // Add Alias label.
12054
12055 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
12056 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
12057
12058 // Add Count label.
12059
12060 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
12061
12062 // Add TTL label.
12063
12064 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
12065
12066 // Add Tag label.
12067
12068 SNPrintF_Add( &ptr, end, "tag-%s.",
12069 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
12070
12071 // Add IPv4 or IPv6 label if necessary.
12072
12073 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
12074 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
12075
12076 // Finally, add the d.test. labels.
12077
12078 SNPrintF_Add( &ptr, end, "d.test." );
12079
12080 // Create item.
12081
12082 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
12083 require_noerr( err, exit );
12084
12085 newItemList = item;
12086 itemPtr = &item->next;
12087
12088 // Create repeat items.
12089
12090 for( i = 1; i < inItemCount; ++i )
12091 {
12092 err = GAITestItemDup( item, &item2 );
12093 require_noerr( err, exit );
12094
12095 *itemPtr = item2;
12096 itemPtr = &item2->next;
12097 }
12098
12099 // Append to test case's item list.
12100
12101 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12102 *itemPtr = newItemList;
12103 newItemList = NULL;
12104
12105 exit:
12106 while( ( item = newItemList ) != NULL )
12107 {
12108 newItemList = item->next;
12109 GAITestItemFree( item );
12110 }
12111 return( err );
12112 }
12113
12114 //===========================================================================================================================
12115 // GAITestCaseAddLocalHostItem
12116 //===========================================================================================================================
12117
12118 static OSStatus
12119 GAITestCaseAddLocalHostItem(
12120 GAITestCase * inCase,
12121 GAITestAddrType inWantAddrs,
12122 unsigned int inTimeLimitMs,
12123 unsigned int inItemCount )
12124 {
12125 OSStatus err;
12126 GAITestItem * item;
12127 GAITestItem * item2;
12128 GAITestItem * newItemList = NULL;
12129 GAITestItem ** itemPtr;
12130 unsigned int i;
12131
12132 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
12133
12134 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
12135 require_noerr( err, exit );
12136
12137 newItemList = item;
12138 itemPtr = &item->next;
12139
12140 // Create repeat items.
12141
12142 for( i = 1; i < inItemCount; ++i )
12143 {
12144 err = GAITestItemDup( item, &item2 );
12145 require_noerr( err, exit );
12146
12147 *itemPtr = item2;
12148 itemPtr = &item2->next;
12149 }
12150
12151 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12152 *itemPtr = newItemList;
12153 newItemList = NULL;
12154
12155 exit:
12156 while( ( item = newItemList ) != NULL )
12157 {
12158 newItemList = item->next;
12159 GAITestItemFree( item );
12160 }
12161 return( err );
12162 }
12163
12164 //===========================================================================================================================
12165 // GAITestItemCreate
12166 //===========================================================================================================================
12167
12168 static OSStatus
12169 GAITestItemCreate(
12170 const char * inName,
12171 unsigned int inAddressCount,
12172 GAITestAddrType inHasAddrs,
12173 GAITestAddrType inWantAddrs,
12174 unsigned int inTimeLimitMs,
12175 GAITestItem ** outItem )
12176 {
12177 OSStatus err;
12178 GAITestItem * obj = NULL;
12179
12180 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
12181 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12182 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
12183
12184 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12185 require_action( obj, exit, err = kNoMemoryErr );
12186
12187 obj->name = strdup( inName );
12188 require_action( obj->name, exit, err = kNoMemoryErr );
12189
12190 obj->addressCount = inAddressCount;
12191 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12192 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12193 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12194 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12195 obj->error = kInProgressErr;
12196 obj->timeLimitMs = inTimeLimitMs;
12197
12198 *outItem = obj;
12199 obj = NULL;
12200 err = kNoErr;
12201
12202 exit:
12203 if( obj ) GAITestItemFree( obj );
12204 return( err );
12205 }
12206
12207 //===========================================================================================================================
12208 // GAITestItemDup
12209 //===========================================================================================================================
12210
12211 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
12212 {
12213 OSStatus err;
12214 GAITestItem * obj;
12215
12216 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12217 require_action( obj, exit, err = kNoMemoryErr );
12218
12219 *obj = *inItem;
12220 obj->next = NULL;
12221 if( inItem->name )
12222 {
12223 obj->name = strdup( inItem->name );
12224 require_action( obj->name, exit, err = kNoMemoryErr );
12225 }
12226
12227 *outItem = obj;
12228 obj = NULL;
12229 err = kNoErr;
12230
12231 exit:
12232 if( obj ) GAITestItemFree( obj );
12233 return( err );
12234 }
12235
12236 //===========================================================================================================================
12237 // GAITestItemFree
12238 //===========================================================================================================================
12239
12240 static void GAITestItemFree( GAITestItem *inItem )
12241 {
12242 ForgetMem( &inItem->name );
12243 free( inItem );
12244 }
12245
12246 //===========================================================================================================================
12247 // MDNSDiscoveryTestCmd
12248 //===========================================================================================================================
12249
12250 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12251
12252 typedef struct
12253 {
12254 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12255 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
12256 NanoTime64 startTime; // When the test started.
12257 NanoTime64 endTime; // When the test ended.
12258 pid_t replierPID; // PID of mDNS replier.
12259 uint32_t ifIndex; // Index of interface to run the replier on.
12260 unsigned int instanceCount; // Desired number of service instances.
12261 unsigned int txtSize; // Desired size of each service instance's TXT record data.
12262 unsigned int recordCountA; // Desired number of A records per replier hostname.
12263 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
12264 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
12265 double ucastDropRate; // Replier's probability of dropping a unicast response.
12266 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
12267 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
12268 Boolean useIPv4; // True if the replier is to use IPv4.
12269 Boolean useIPv6; // True if the replier is to use IPv6.
12270 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
12271 char * replierCommand; // Command used to run the replier.
12272 char * serviceType; // Type of services to browse for.
12273 ServiceBrowserRef browser; // Service browser.
12274 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
12275 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12276 OutputFormatType outputFormat; // Format of test results output.
12277 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
12278 char hostname[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12279 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12280
12281 } MDNSDiscoveryTestContext;
12282
12283 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
12284 static void DNSSD_API
12285 _MDNSDiscoveryTestAboutQueryCallback(
12286 DNSServiceRef inSDRef,
12287 DNSServiceFlags inFlags,
12288 uint32_t inInterfaceIndex,
12289 DNSServiceErrorType inError,
12290 const char * inFullName,
12291 uint16_t inType,
12292 uint16_t inClass,
12293 uint16_t inRDataLen,
12294 const void * inRDataPtr,
12295 uint32_t inTTL,
12296 void * inContext );
12297 static void
12298 _MDNSDiscoveryTestServiceBrowserCallback(
12299 ServiceBrowserResults * inResults,
12300 OSStatus inError,
12301 void * inContext );
12302 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
12303
12304 static void MDNSDiscoveryTestCmd( void )
12305 {
12306 OSStatus err;
12307 MDNSDiscoveryTestContext * context;
12308 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
12309
12310 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
12311 require_action( context, exit, err = kNoMemoryErr );
12312
12313 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
12314 require_noerr_quiet( err, exit );
12315
12316 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
12317 require_noerr_quiet( err, exit );
12318
12319 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
12320 require_noerr_quiet( err, exit );
12321
12322 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
12323 require_noerr_quiet( err, exit );
12324
12325 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
12326 require_noerr_quiet( err, exit );
12327
12328 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
12329 require_noerr_quiet( err, exit );
12330
12331 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
12332 require_noerr_quiet( err, exit );
12333
12334 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
12335 require_noerr_quiet( err, exit );
12336
12337 context->replierPID = -1;
12338 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
12339 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
12340 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
12341 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
12342 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
12343 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
12344 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
12345 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
12346 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
12347 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
12348 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
12349 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
12350 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
12351
12352 if( gMDNSDiscoveryTest_Interface )
12353 {
12354 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
12355 require_noerr_quiet( err, exit );
12356 }
12357 else
12358 {
12359 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
12360 require_noerr_quiet( err, exit );
12361 }
12362
12363 err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
12364 require_noerr_quiet( err, exit );
12365
12366 if( gMDNSDiscoveryTest_FlushCache )
12367 {
12368 err = CheckRootUser();
12369 require_noerr_quiet( err, exit );
12370
12371 err = systemf( NULL, "killall -HUP mDNSResponder" );
12372 require_noerr( err, exit );
12373 sleep( 1 );
12374 context->flushedCache = true;
12375 }
12376
12377 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
12378 context->hostname );
12379 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
12380
12381 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
12382 require_action( context->serviceType, exit, err = kUnknownErr );
12383
12384 ASPrintF( &context->replierCommand,
12385 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12386 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12387 (int64_t) getpid(),
12388 context->ifIndex,
12389 context->hostname,
12390 context->tag,
12391 context->instanceCount,
12392 context->recordCountA,
12393 context->recordCountAAAA,
12394 context->ucastDropRate,
12395 context->mcastDropRate,
12396 context->maxDropCount,
12397 context->noAdditionals, " --noAdditionals",
12398 context->useIPv4, " --ipv4",
12399 context->useIPv6, " --ipv6" );
12400 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
12401
12402 err = SpawnCommand( &context->replierPID, "%s", context->replierCommand );
12403 require_noerr_quiet( err, exit );
12404
12405 // Query for the replier's about TXT record. A response means it's fully up and running.
12406
12407 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
12408 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
12409 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
12410 require_noerr( err, exit );
12411
12412 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
12413 require_noerr( err, exit );
12414
12415 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
12416 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
12417 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
12418 require_noerr( err, exit );
12419 dispatch_resume( context->queryTimer );
12420
12421 context->startTime = NanoTimeGetCurrent();
12422 dispatch_main();
12423
12424 exit:
12425 exit( 1 );
12426 }
12427
12428 //===========================================================================================================================
12429 // _MDNSDiscoveryTestFirstQueryTimeout
12430 //===========================================================================================================================
12431
12432 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
12433 {
12434 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12435
12436 dispatch_source_forget( &context->queryTimer );
12437
12438 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12439 exit( 1 );
12440 }
12441
12442 //===========================================================================================================================
12443 // _MDNSDiscoveryTestAboutQueryCallback
12444 //===========================================================================================================================
12445
12446 static void DNSSD_API
12447 _MDNSDiscoveryTestAboutQueryCallback(
12448 DNSServiceRef inSDRef,
12449 DNSServiceFlags inFlags,
12450 uint32_t inInterfaceIndex,
12451 DNSServiceErrorType inError,
12452 const char * inFullName,
12453 uint16_t inType,
12454 uint16_t inClass,
12455 uint16_t inRDataLen,
12456 const void * inRDataPtr,
12457 uint32_t inTTL,
12458 void * inContext )
12459 {
12460 OSStatus err;
12461 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12462
12463 Unused( inSDRef );
12464 Unused( inInterfaceIndex );
12465 Unused( inFullName );
12466 Unused( inType );
12467 Unused( inClass );
12468 Unused( inRDataLen );
12469 Unused( inRDataPtr );
12470 Unused( inTTL );
12471
12472 err = inError;
12473 require_noerr( err, exit );
12474 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
12475
12476 DNSServiceForget( &context->query );
12477 dispatch_source_forget( &context->queryTimer );
12478
12479 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
12480 require_noerr( err, exit );
12481
12482 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
12483 require_noerr( err, exit );
12484
12485 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
12486 ServiceBrowserStart( context->browser );
12487
12488 exit:
12489 if( err ) exit( 1 );
12490 }
12491
12492 //===========================================================================================================================
12493 // _MDNSDiscoveryTestServiceBrowserCallback
12494 //===========================================================================================================================
12495
12496 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12497 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12498 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12499 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12500 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12501 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12502 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12503 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12504 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12505 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12506 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12507
12508 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12509 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12510 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12511 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12512 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12513 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12514 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12515 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12516 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12517 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12518
12519 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12520 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12521
12522 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12523 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12524 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12525 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12526 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12527 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12528 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12529
12530 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
12531 {
12532 OSStatus err;
12533 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12534 const SBRDomain * domain;
12535 const SBRServiceType * type;
12536 const SBRServiceInstance * instance;
12537 const SBRServiceInstance ** instanceArray = NULL;
12538 const SBRIPAddress * ipaddr;
12539 size_t hostnameLen;
12540 const char * ptr;
12541 const char * end;
12542 unsigned int i;
12543 uint32_t u32;
12544 CFMutableArrayRef unexpectedInstances;
12545 CFMutableArrayRef missingInstances;
12546 CFMutableArrayRef incorrectInstances;
12547 CFMutableDictionaryRef plist = NULL;
12548 CFMutableDictionaryRef badDict = NULL;
12549 CFMutableArrayRef unexpectedAddrs = NULL;
12550 CFMutableArrayRef missingAddrs = NULL;
12551 uint64_t maxResolveTimeUs;
12552 int success = false;
12553 char startTime[ 32 ];
12554 char endTime[ 32 ];
12555
12556 context->endTime = NanoTimeGetCurrent();
12557
12558 err = inError;
12559 require_noerr( err, exit );
12560
12561 _NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
12562 _NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
12563 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
12564 "{"
12565 "%kO="
12566 "{"
12567 "%kO=%s" // replierCommand
12568 "%kO=%lli" // txtSize
12569 "%kO=%lli" // instanceCount
12570 "%kO=%lli" // recordCountA
12571 "%kO=%lli" // recordCountAAAA
12572 "%kO=%s" // hostname
12573 "%kO=%b" // noAdditionals
12574 "%kO=%f" // ucastDropRate
12575 "%kO=%f" // mcastDropRate
12576 "%kO=%i" // maxDropCount
12577 "}"
12578 "%kO=%s" // startTime
12579 "%kO=%s" // endTime
12580 "%kO=%lli" // browseTimeSecs
12581 "%kO=%s" // serviceType
12582 "%kO=%b" // flushedCache
12583 "%kO=[%@]" // unexpectedInstances
12584 "%kO=[%@]" // missingInstances
12585 "%kO=[%@]" // incorrectInstances
12586 "}",
12587 kMDNSDiscoveryTestResultsKey_ReplierInfo,
12588 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
12589 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
12590 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
12591 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
12592 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
12593 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
12594 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
12595 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
12596 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
12597 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
12598 kMDNSDiscoveryTestResultsKey_StartTime, startTime,
12599 kMDNSDiscoveryTestResultsKey_EndTime, endTime,
12600 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
12601 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
12602 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
12603 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
12604 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
12605 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
12606 require_noerr( err, exit );
12607
12608 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
12609 require_action( domain, exit, err = kInternalErr );
12610
12611 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
12612 require_action( type, exit, err = kInternalErr );
12613
12614 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
12615 require_action( instanceArray, exit, err = kNoMemoryErr );
12616
12617 hostnameLen = strlen( context->hostname );
12618 for( instance = type->instanceList; instance; instance = instance->next )
12619 {
12620 unsigned int instanceNumber = 0;
12621
12622 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
12623 {
12624 ptr = &instance->name[ hostnameLen ];
12625 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
12626 {
12627 ptr += 2;
12628 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12629 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12630 {
12631 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
12632 {
12633 instanceNumber = u32;
12634 }
12635 }
12636 }
12637 else if( *ptr == '\0' )
12638 {
12639 instanceNumber = 1;
12640 }
12641 }
12642 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
12643 {
12644 check( !instanceArray[ instanceNumber - 1 ] );
12645 instanceArray[ instanceNumber - 1 ] = instance;
12646 }
12647 else
12648 {
12649 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
12650 "{"
12651 "%kO=%s"
12652 "%kO=%lli"
12653 "}",
12654 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
12655 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
12656 require_noerr( err, exit );
12657 }
12658 }
12659
12660 maxResolveTimeUs = 0;
12661 for( i = 1; i <= context->instanceCount; ++i )
12662 {
12663 int isHostnameValid;
12664 int isTXTValid;
12665
12666 instance = instanceArray[ i - 1 ];
12667 if( !instance )
12668 {
12669 if( i == 1 )
12670 {
12671 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
12672 require_noerr( err, exit );
12673 }
12674 else
12675 {
12676 char * instanceName = NULL;
12677
12678 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
12679 require_action( instanceName, exit, err = kUnknownErr );
12680
12681 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
12682 free( instanceName );
12683 require_noerr( err, exit );
12684 }
12685 continue;
12686 }
12687
12688 if( !instance->hostname )
12689 {
12690 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
12691 "{"
12692 "%kO=%s"
12693 "%kO=%b"
12694 "}",
12695 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12696 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
12697 require_noerr( err, exit );
12698 continue;
12699 }
12700
12701 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
12702 require_action( badDict, exit, err = kNoMemoryErr );
12703
12704 isHostnameValid = false;
12705 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
12706 {
12707 ptr = &instance->hostname[ hostnameLen ];
12708 if( i == 1 )
12709 {
12710 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
12711 }
12712 else if( *ptr == '-' )
12713 {
12714 ++ptr;
12715 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12716 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12717 {
12718 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
12719 }
12720 }
12721 }
12722 if( !isHostnameValid )
12723 {
12724 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
12725 kSizeCString );
12726 require_noerr( err, exit );
12727 }
12728
12729 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
12730 {
12731 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
12732 require_noerr( err, exit );
12733 }
12734
12735 isTXTValid = false;
12736 if( instance->txtLen == context->txtSize )
12737 {
12738 uint8_t name[ kDomainNameLengthMax ];
12739
12740 err = DomainNameFromString( name, instance->name, NULL );
12741 require_noerr( err, exit );
12742
12743 err = DomainNameAppendString( name, type->name, NULL );
12744 require_noerr( err, exit );
12745
12746 err = DomainNameAppendString( name, "local", NULL );
12747 require_noerr( err, exit );
12748
12749 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
12750 }
12751 if( !isTXTValid )
12752 {
12753 char * hexStr = NULL;
12754
12755 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
12756 require_action( hexStr, exit, err = kUnknownErr );
12757
12758 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
12759 free( hexStr );
12760 require_noerr( err, exit );
12761 }
12762
12763 if( isHostnameValid )
12764 {
12765 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
12766 unsigned int j;
12767 uint8_t addrV4[ 4 ];
12768 uint8_t addrV6[ 16 ];
12769
12770 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
12771 else addrV4Bitmap = ~UINT64_C( 0 );
12772
12773 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
12774 else addrV6Bitmap = ~UINT64_C( 0 );
12775
12776 addrV4[ 0 ] = 0;
12777 WriteBig16( &addrV4[ 1 ], i );
12778 addrV4[ 3 ] = 0;
12779
12780 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
12781 WriteBig16( &addrV6[ 12 ], i );
12782
12783 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12784 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
12785
12786 resolveTimeUs = 0;
12787 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
12788 {
12789 const uint8_t * addrPtr;
12790 unsigned int lsb;
12791 int isAddrValid = false;
12792
12793 if( ipaddr->sip.sa.sa_family == AF_INET )
12794 {
12795 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
12796 lsb = addrPtr[ 3 ];
12797 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
12798 {
12799 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12800 addrV4Bitmap &= ~bitmask;
12801 isAddrValid = true;
12802 }
12803 }
12804 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
12805 {
12806 addrPtr = ipaddr->sip.v6.sin6_addr.s6_addr;
12807 lsb = addrPtr[ 15 ];
12808 if( ( memcmp( addrPtr, addrV6, 15 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
12809 {
12810 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12811 addrV6Bitmap &= ~bitmask;
12812 isAddrValid = true;
12813 }
12814 }
12815 if( isAddrValid )
12816 {
12817 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
12818 }
12819 else
12820 {
12821 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
12822 require_noerr( err, exit );
12823 }
12824 }
12825
12826 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
12827 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
12828
12829 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
12830 {
12831 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
12832 }
12833 ForgetCF( &unexpectedAddrs );
12834
12835 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12836 require_action( missingAddrs, exit, err = kNoMemoryErr );
12837
12838 for( j = 1; addrV4Bitmap != 0; ++j )
12839 {
12840 bitmask = UINT64_C( 1 ) << ( j - 1 );
12841 if( addrV4Bitmap & bitmask )
12842 {
12843 addrV4Bitmap &= ~bitmask;
12844 addrV4[ 3 ] = (uint8_t) j;
12845 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
12846 require_noerr( err, exit );
12847 }
12848 }
12849 for( j = 1; addrV6Bitmap != 0; ++j )
12850 {
12851 bitmask = UINT64_C( 1 ) << ( j - 1 );
12852 if( addrV6Bitmap & bitmask )
12853 {
12854 addrV6Bitmap &= ~bitmask;
12855 addrV6[ 15 ] = (uint8_t) j;
12856 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.16a", addrV6 );
12857 require_noerr( err, exit );
12858 }
12859 }
12860
12861 if( CFArrayGetCount( missingAddrs ) > 0 )
12862 {
12863 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
12864 }
12865 ForgetCF( &missingAddrs );
12866 }
12867
12868 if( CFDictionaryGetCount( badDict ) > 0 )
12869 {
12870 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12871 kSizeCString );
12872 require_noerr( err, exit );
12873
12874 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
12875 CFArrayAppendValue( incorrectInstances, badDict );
12876 }
12877 ForgetCF( &badDict );
12878 }
12879
12880 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
12881 ( CFArrayGetCount( missingInstances ) == 0 ) &&
12882 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
12883 {
12884 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
12885 require_noerr( err, exit );
12886 success = true;
12887 }
12888 else
12889 {
12890 success = false;
12891 }
12892 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
12893
12894 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
12895 require_noerr_quiet( err, exit );
12896
12897 exit:
12898 ForgetCF( &context->browser );
12899 if( context->replierPID != -1 )
12900 {
12901 kill( context->replierPID, SIGTERM );
12902 context->replierPID = -1;
12903 }
12904 FreeNullSafe( instanceArray );
12905 CFReleaseNullSafe( plist );
12906 CFReleaseNullSafe( badDict );
12907 CFReleaseNullSafe( unexpectedAddrs );
12908 CFReleaseNullSafe( missingAddrs );
12909 exit( err ? 1 : ( success ? 0 : 2 ) );
12910 }
12911
12912 //===========================================================================================================================
12913 // _MDNSDiscoveryTestTXTRecordIsValid
12914 //===========================================================================================================================
12915
12916 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
12917 {
12918 uint32_t hash;
12919 int n;
12920 const uint8_t * ptr;
12921 size_t i, wholeCount, remCount;
12922 uint8_t txtStr[ 16 ];
12923
12924 if( inTXTLen == 0 ) return( false );
12925
12926 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
12927
12928 txtStr[ 0 ] = 15;
12929 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12930 check( n == 15 );
12931
12932 ptr = inTXTPtr;
12933 wholeCount = inTXTLen / 16;
12934 for( i = 0; i < wholeCount; ++i )
12935 {
12936 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
12937 ptr += 16;
12938 }
12939
12940 remCount = inTXTLen % 16;
12941 if( remCount > 0 )
12942 {
12943 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12944 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
12945 ptr += remCount;
12946 }
12947 check( ptr == &inTXTPtr[ inTXTLen ] );
12948 return( true );
12949 }
12950
12951 //===========================================================================================================================
12952 // DotLocalTestCmd
12953 //===========================================================================================================================
12954
12955 #define kDotLocalTestPreparationTimeLimitSecs 5
12956 #define kDotLocalTestSubtestDurationSecs 5
12957
12958 // Constants for SRV record query subtest.
12959
12960 #define kDotLocalTestSRV_Priority 1
12961 #define kDotLocalTestSRV_Weight 0
12962 #define kDotLocalTestSRV_Port 80
12963 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12964 #define kDotLocalTestSRV_TargetStr "www.example.com."
12965 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12966
12967 typedef enum
12968 {
12969 kDotLocalTestState_Unset = 0,
12970 kDotLocalTestState_Preparing = 1,
12971 kDotLocalTestState_GAIMDNSOnly = 2,
12972 kDotLocalTestState_GAIDNSOnly = 3,
12973 kDotLocalTestState_GAIBoth = 4,
12974 kDotLocalTestState_GAINeither = 5,
12975 kDotLocalTestState_GAINoSuchRecord = 6,
12976 kDotLocalTestState_QuerySRV = 7,
12977 kDotLocalTestState_Done = 8
12978
12979 } DotLocalTestState;
12980
12981 typedef struct
12982 {
12983 const char * testDesc; // Description of the current subtest.
12984 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
12985 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
12986 NanoTime64 startTime; // Timestamp of when the subtest started.
12987 NanoTime64 endTime; // Timestamp of when the subtest ended.
12988 CFMutableArrayRef correctResults; // Operation results that were expected.
12989 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
12990 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
12991 OSStatus error; // Subtest's error code.
12992 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12993 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12994 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12995 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12996 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
12997 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
12998 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
12999 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
13000 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13001 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13002 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13003 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13004 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
13005
13006 } DotLocalSubtest;
13007
13008 typedef struct
13009 {
13010 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
13011 DotLocalSubtest * subtest; // Current subtest's state.
13012 DNSServiceRef connection; // Shared connection for DNS-SD operations.
13013 DNSServiceRef op; // Reference for the current DNS-SD operation.
13014 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
13015 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13016 char * replierCmd; // Command used to invoke the mdnsreplier.
13017 char * serverCmd; // Command used to invoke the test DNS server.
13018 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
13019 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13020 NanoTime64 startTime; // Timestamp for when the test started.
13021 NanoTime64 endTime; // Timestamp for when the test ended.
13022 DotLocalTestState state; // The test's current state.
13023 pid_t replierPID; // PID of spawned mdnsreplier.
13024 pid_t serverPID; // PID of spawned test DNS server.
13025 uint32_t ifIndex; // Interface index used for mdnsreplier.
13026 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
13027 OutputFormatType outputFormat; // Format of test results output.
13028 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
13029 Boolean serverIsReady; // True if response was received for test DNS server probe query.
13030 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
13031 Boolean testFailed; // True if at least one subtest failed.
13032 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13033 // The format of this string is "dotlocal-test-<six random chars>".
13034 } DotLocalTestContext;
13035
13036 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
13037 static void DNSSD_API
13038 _DotLocalTestProbeQueryRecordCallback(
13039 DNSServiceRef inSDRef,
13040 DNSServiceFlags inFlags,
13041 uint32_t inInterfaceIndex,
13042 DNSServiceErrorType inError,
13043 const char * inFullName,
13044 uint16_t inType,
13045 uint16_t inClass,
13046 uint16_t inRDataLen,
13047 const void * inRDataPtr,
13048 uint32_t inTTL,
13049 void * inContext );
13050 static void DNSSD_API
13051 _DotLocalTestRegisterRecordCallback(
13052 DNSServiceRef inSDRef,
13053 DNSRecordRef inRecordRef,
13054 DNSServiceFlags inFlags,
13055 DNSServiceErrorType inError,
13056 void * inContext );
13057 static void _DotLocalTestTimerHandler( void *inContext );
13058 static void DNSSD_API
13059 _DotLocalTestGAICallback(
13060 DNSServiceRef inSDRef,
13061 DNSServiceFlags inFlags,
13062 uint32_t inInterfaceIndex,
13063 DNSServiceErrorType inError,
13064 const char * inHostname,
13065 const struct sockaddr * inSockAddr,
13066 uint32_t inTTL,
13067 void * inContext );
13068 static void DNSSD_API
13069 _DotLocalTestQueryRecordCallback(
13070 DNSServiceRef inSDRef,
13071 DNSServiceFlags inFlags,
13072 uint32_t inInterfaceIndex,
13073 DNSServiceErrorType inError,
13074 const char * inFullName,
13075 uint16_t inType,
13076 uint16_t inClass,
13077 uint16_t inRDataLen,
13078 const void * inRDataPtr,
13079 uint32_t inTTL,
13080 void * inContext );
13081
13082 static void DotLocalTestCmd( void )
13083 {
13084 OSStatus err;
13085 DotLocalTestContext * context;
13086 uint8_t * rdataPtr;
13087 size_t rdataLen;
13088 DNSServiceFlags flags;
13089 char queryName[ 64 ];
13090 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
13091
13092 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
13093 require_action( context, exit, err = kNoMemoryErr );
13094
13095 context->startTime = NanoTimeGetCurrent();
13096 context->endTime = kNanoTime_Invalid;
13097
13098 context->state = kDotLocalTestState_Preparing;
13099
13100 if( gDotLocalTest_Interface )
13101 {
13102 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
13103 require_noerr_quiet( err, exit );
13104 }
13105 else
13106 {
13107 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
13108 require_noerr_quiet( err, exit );
13109 }
13110
13111 if( gDotLocalTest_OutputFilePath )
13112 {
13113 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
13114 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
13115 }
13116
13117 err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
13118 require_noerr_quiet( err, exit );
13119
13120 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13121 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
13122
13123 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13124 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
13125
13126 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
13127 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
13128
13129 // Spawn an mdnsreplier.
13130
13131 ASPrintF( &context->replierCmd,
13132 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13133 " --countAAAA 1",
13134 (int64_t) getpid(), context->ifIndex, context->labelStr,
13135 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
13136 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
13137
13138 err = SpawnCommand( &context->replierPID, "%s", context->replierCmd );
13139 require_noerr( err, exit );
13140
13141 // Spawn a test DNS server
13142
13143 ASPrintF( &context->serverCmd,
13144 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13145 (int64_t) getpid(), context->labelStr );
13146 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
13147
13148 err = SpawnCommand( &context->serverPID, "%s", context->serverCmd );
13149 require_noerr( err, exit );
13150
13151 // Create a shared DNS-SD connection.
13152
13153 err = DNSServiceCreateConnection( &context->connection );
13154 require_noerr( err, exit );
13155
13156 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
13157 require_noerr( err, exit );
13158
13159 // Create probe query for DNS server, i.e., query for any name that has an A record.
13160
13161 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
13162
13163 flags = kDNSServiceFlagsShareConnection;
13164 #if( TARGET_OS_WATCH )
13165 flags |= kDNSServiceFlagsPathEvaluationDone;
13166 #endif
13167
13168 context->op = context->connection;
13169 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
13170 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
13171 require_noerr( err, exit );
13172
13173 // Create probe query for mdnsreplier's "about" TXT record.
13174
13175 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
13176
13177 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
13178 #if( TARGET_OS_WATCH )
13179 flags |= kDNSServiceFlagsPathEvaluationDone;
13180 #endif
13181
13182 context->op2 = context->connection;
13183 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
13184 _DotLocalTestProbeQueryRecordCallback, context );
13185 require_noerr( err, exit );
13186
13187 // Register a dummy local. SOA record.
13188
13189 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
13190 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
13191 require_noerr( err, exit );
13192
13193 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
13194 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN, 1,
13195 rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
13196 require_noerr( err, exit );
13197
13198 // Start timer for probe responses and SOA record registration.
13199
13200 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
13201 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13202 _DotLocalTestTimerHandler, context, &context->timer );
13203 require_noerr( err, exit );
13204 dispatch_resume( context->timer );
13205
13206 dispatch_main();
13207
13208 exit:
13209 if( err ) ErrQuit( 1, "error: %#m\n", err );
13210 }
13211
13212 //===========================================================================================================================
13213 // _DotLocalTestStateMachine
13214 //===========================================================================================================================
13215
13216 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
13217 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
13218 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
13219 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
13220 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
13221
13222 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
13223 {
13224 OSStatus err;
13225 DotLocalTestState nextState;
13226
13227 DNSServiceForget( &inContext->op );
13228 DNSServiceForget( &inContext->op2 );
13229 dispatch_source_forget( &inContext->timer );
13230
13231 switch( inContext->state )
13232 {
13233 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
13234 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
13235 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
13236 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
13237 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
13238 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
13239 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
13240 default: err = kStateErr; goto exit;
13241 }
13242
13243 if( inContext->state == kDotLocalTestState_Preparing )
13244 {
13245 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
13246 {
13247 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13248 YesNoStr( inContext->registeredSOA ),
13249 YesNoStr( inContext->serverIsReady ),
13250 YesNoStr( inContext->replierIsReady ) );
13251 err = kNotPreparedErr;
13252 goto exit;
13253 }
13254 }
13255 else
13256 {
13257 err = _DotLocalTestFinalizeSubtest( inContext );
13258 require_noerr( err, exit );
13259 }
13260
13261 inContext->state = nextState;
13262 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
13263 err = _DotLocalTestStartSubtest( inContext );
13264
13265 exit:
13266 if( err ) ErrQuit( 1, "error: %#m\n", err );
13267 }
13268
13269 //===========================================================================================================================
13270 // _DotLocalSubtestCreate
13271 //===========================================================================================================================
13272
13273 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
13274 {
13275 OSStatus err;
13276 DotLocalSubtest * obj;
13277
13278 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
13279 require_action( obj, exit, err = kNoMemoryErr );
13280
13281 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13282 require_action( obj->correctResults, exit, err = kNoMemoryErr );
13283
13284 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13285 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
13286
13287 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13288 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
13289
13290 *outSubtest = obj;
13291 obj = NULL;
13292 err = kNoErr;
13293
13294 exit:
13295 if( obj ) _DotLocalSubtestFree( obj );
13296 return( err );
13297 }
13298
13299 //===========================================================================================================================
13300 // _DotLocalSubtestFree
13301 //===========================================================================================================================
13302
13303 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
13304 {
13305 ForgetMem( &inSubtest->queryName );
13306 ForgetCF( &inSubtest->correctResults );
13307 ForgetCF( &inSubtest->duplicateResults );
13308 ForgetCF( &inSubtest->unexpectedResults );
13309 free( inSubtest );
13310 }
13311
13312 //===========================================================================================================================
13313 // _DotLocalTestStartSubtest
13314 //===========================================================================================================================
13315
13316 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
13317 {
13318 OSStatus err;
13319 DotLocalSubtest * subtest = NULL;
13320 DNSServiceRef op = NULL;
13321 DNSServiceFlags flags;
13322
13323 err = _DotLocalSubtestCreate( &subtest );
13324 require_noerr( err, exit );
13325
13326 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
13327 {
13328 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
13329 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13330
13331 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13332 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13333
13334 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
13335 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::2:1
13336 subtest->addrMDNSv6[ 13 ] = 2;
13337 subtest->addrMDNSv6[ 15 ] = 1;
13338
13339 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
13340 }
13341
13342 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
13343 {
13344 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
13345 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13346
13347 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13348 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13349
13350 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13351 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13352 subtest->addrDNSv6[ 15 ] = 1;
13353
13354 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
13355 }
13356
13357 else if( inContext->state == kDotLocalTestState_GAIBoth )
13358 {
13359 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
13360 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13361
13362 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13363 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13364 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13365 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13366
13367 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13368 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13369 subtest->addrDNSv6[ 15 ] = 1;
13370
13371 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
13372 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::1:1
13373 subtest->addrMDNSv6[ 13 ] = 1;
13374 subtest->addrMDNSv6[ 15 ] = 1;
13375
13376 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
13377 }
13378
13379 else if( inContext->state == kDotLocalTestState_GAINeither )
13380 {
13381 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
13382 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13383
13384 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
13385 }
13386
13387 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
13388 {
13389 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
13390 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13391
13392 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13393 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13394 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
13395 }
13396
13397 else if( inContext->state == kDotLocalTestState_QuerySRV )
13398 {
13399 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13400 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
13401 inContext->labelStr );
13402 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13403
13404 subtest->needSRV = true;
13405 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
13406 }
13407
13408 else
13409 {
13410 err = kStateErr;
13411 goto exit;
13412 }
13413
13414 // Start new operation.
13415
13416 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
13417 #if( TARGET_OS_WATCH )
13418 flags |= kDNSServiceFlagsPathEvaluationDone;
13419 #endif
13420
13421 subtest->startTime = NanoTimeGetCurrent();
13422 subtest->endTime = kNanoTime_Invalid;
13423
13424 if( inContext->state == kDotLocalTestState_QuerySRV )
13425 {
13426 op = inContext->connection;
13427 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
13428 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
13429 require_noerr( err, exit );
13430 }
13431 else
13432 {
13433 op = inContext->connection;
13434 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
13435 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
13436 require_noerr( err, exit );
13437 }
13438
13439 // Start timer.
13440
13441 check( !inContext->timer );
13442 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
13443 INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13444 _DotLocalTestTimerHandler, inContext, &inContext->timer );
13445 require_noerr( err, exit );
13446 dispatch_resume( inContext->timer );
13447
13448 check( !inContext->op );
13449 inContext->op = op;
13450 op = NULL;
13451
13452 check( !inContext->subtest );
13453 inContext->subtest = subtest;
13454 subtest = NULL;
13455
13456 exit:
13457 if( subtest ) _DotLocalSubtestFree( subtest );
13458 if( op ) DNSServiceRefDeallocate( op );
13459 return( err );
13460 }
13461
13462 //===========================================================================================================================
13463 // _DotLocalTestFinalizeSubtest
13464 //===========================================================================================================================
13465
13466 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13467 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13468 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13469 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13470 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13471 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13472 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13473 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13474 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13475 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13476 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13477 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13478 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13479 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13480 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13481
13482 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
13483 {
13484 OSStatus err;
13485 DotLocalSubtest * subtest;
13486 CFMutableDictionaryRef reportDict;
13487 CFMutableDictionaryRef resultsDict;
13488 CFMutableArrayRef missingResults, reportArray;
13489 char startTime[ 32 ];
13490 char endTime[ 32 ];
13491
13492 subtest = inContext->subtest;
13493 inContext->subtest = NULL;
13494
13495 subtest->endTime = NanoTimeGetCurrent();
13496 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
13497 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
13498
13499 reportDict = NULL;
13500 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
13501 "{"
13502 "%kO=%s" // startTime
13503 "%kO=%s" // endTime
13504 "%kO=%s" // queryName
13505 "%kO=%s" // description
13506 "%kO={%@}" // results
13507 "}",
13508 kDotLocalTestReportKey_StartTime, startTime,
13509 kDotLocalTestReportKey_EndTime, endTime,
13510 kDotLocalTestReportKey_QueryName, subtest->queryName,
13511 kDotLocalTestReportKey_Description, subtest->testDesc,
13512 kDotLocalTestReportKey_Results, &resultsDict );
13513 require_noerr( err, exit );
13514
13515 missingResults = NULL;
13516 switch( inContext->state )
13517 {
13518 case kDotLocalTestState_GAIMDNSOnly:
13519 case kDotLocalTestState_GAIDNSOnly:
13520 case kDotLocalTestState_GAIBoth:
13521 case kDotLocalTestState_GAINeither:
13522 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
13523 {
13524 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13525 "["
13526 "%.4a" // Expected DNS IPv4 address
13527 "%.16a" // Expected DNS IPv6 address
13528 "%.4a" // Expected MDNS IPv4 address
13529 "%.16a" // Expected MDNS IPv6 address
13530 "]",
13531 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
13532 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
13533 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
13534 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
13535 require_noerr( err, exit );
13536 }
13537 break;
13538
13539 case kDotLocalTestState_QuerySRV:
13540 if( subtest->needSRV )
13541 {
13542 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13543 "["
13544 "%s" // Expected SRV record data as a string.
13545 "]",
13546 kDotLocalTestSRV_ResultStr );
13547 require_noerr( err, exit );
13548 }
13549 break;
13550
13551 case kDotLocalTestState_GAINoSuchRecord:
13552 if( subtest->needDNSv4 || subtest->needDNSv6 )
13553 {
13554 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13555 "["
13556 "%s" // No Such Record (A)
13557 "%s" // No Such Record (AAAA)
13558 "]",
13559 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
13560 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
13561 require_noerr( err, exit );
13562 }
13563 break;
13564
13565 default:
13566 err = kStateErr;
13567 goto exit;
13568 }
13569
13570 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
13571
13572 if( missingResults )
13573 {
13574 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
13575 ForgetCF( &missingResults );
13576 if( !subtest->error ) subtest->error = kNotFoundErr;
13577 }
13578
13579 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
13580 {
13581 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
13582 if( !subtest->error ) subtest->error = kUnexpectedErr;
13583 }
13584
13585 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
13586 {
13587 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
13588 if( !subtest->error ) subtest->error = kDuplicateErr;
13589 }
13590
13591 if( subtest->error ) inContext->testFailed = true;
13592 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
13593 require_noerr( err, exit );
13594
13595 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
13596 CFArrayAppendValue( reportArray, reportDict );
13597
13598 exit:
13599 _DotLocalSubtestFree( subtest );
13600 CFReleaseNullSafe( reportDict );
13601 return( err );
13602 }
13603
13604 //===========================================================================================================================
13605 // _DotLocalTestFinalizeAndExit
13606 //===========================================================================================================================
13607
13608 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
13609 {
13610 OSStatus err;
13611 CFPropertyListRef plist;
13612 char timestampStart[ 32 ];
13613 char timestampEnd[ 32 ];
13614
13615 check( !inContext->subtest );
13616 inContext->endTime = NanoTimeGetCurrent();
13617
13618 if( inContext->replierPID != -1 )
13619 {
13620 kill( inContext->replierPID, SIGTERM );
13621 inContext->replierPID = -1;
13622 }
13623 if( inContext->serverPID != -1 )
13624 {
13625 kill( inContext->serverPID, SIGTERM );
13626 inContext->serverPID = -1;
13627 }
13628 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
13629 require_noerr( err, exit );
13630
13631 _NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
13632 _NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
13633
13634 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13635 "{"
13636 "%kO=%s" // startTime
13637 "%kO=%s" // endTime
13638 "%kO=%O" // testsGAI
13639 "%kO=%O" // testsQuerySRV
13640 "%kO=%b" // success
13641 "%kO=%s" // replierCmd
13642 "%kO=%s" // serverCmd
13643 "}",
13644 kDotLocalTestReportKey_StartTime, timestampStart,
13645 kDotLocalTestReportKey_EndTime, timestampEnd,
13646 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
13647 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
13648 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
13649 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
13650 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
13651 require_noerr( err, exit );
13652
13653 ForgetCF( &inContext->reportsGAI );
13654 ForgetCF( &inContext->reportsQuerySRV );
13655
13656 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
13657 CFRelease( plist );
13658 require_noerr( err, exit );
13659
13660 exit( inContext->testFailed ? 2 : 0 );
13661
13662 exit:
13663 ErrQuit( 1, "error: %#m\n", err );
13664 }
13665
13666 //===========================================================================================================================
13667 // _DotLocalTestProbeQueryRecordCallback
13668 //===========================================================================================================================
13669
13670 static void DNSSD_API
13671 _DotLocalTestProbeQueryRecordCallback(
13672 DNSServiceRef inSDRef,
13673 DNSServiceFlags inFlags,
13674 uint32_t inInterfaceIndex,
13675 DNSServiceErrorType inError,
13676 const char * inFullName,
13677 uint16_t inType,
13678 uint16_t inClass,
13679 uint16_t inRDataLen,
13680 const void * inRDataPtr,
13681 uint32_t inTTL,
13682 void * inContext )
13683 {
13684 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13685
13686 Unused( inInterfaceIndex );
13687 Unused( inFullName );
13688 Unused( inType );
13689 Unused( inClass );
13690 Unused( inRDataLen );
13691 Unused( inRDataPtr );
13692 Unused( inTTL );
13693
13694 check( context->state == kDotLocalTestState_Preparing );
13695
13696 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
13697
13698 if( inSDRef == context->op )
13699 {
13700 DNSServiceForget( &context->op );
13701 context->serverIsReady = true;
13702 }
13703 else if( inSDRef == context->op2 )
13704 {
13705 DNSServiceForget( &context->op2 );
13706 context->replierIsReady = true;
13707 }
13708
13709 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
13710 {
13711 _DotLocalTestStateMachine( context );
13712 }
13713
13714 exit:
13715 return;
13716 }
13717
13718 //===========================================================================================================================
13719 // _DotLocalTestRegisterRecordCallback
13720 //===========================================================================================================================
13721
13722 static void DNSSD_API
13723 _DotLocalTestRegisterRecordCallback(
13724 DNSServiceRef inSDRef,
13725 DNSRecordRef inRecordRef,
13726 DNSServiceFlags inFlags,
13727 DNSServiceErrorType inError,
13728 void * inContext )
13729 {
13730 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13731
13732 Unused( inSDRef );
13733 Unused( inRecordRef );
13734 Unused( inFlags );
13735
13736 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
13737
13738 if( !context->registeredSOA )
13739 {
13740 context->registeredSOA = true;
13741 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
13742 }
13743 }
13744
13745 //===========================================================================================================================
13746 // _DotLocalTestTimerHandler
13747 //===========================================================================================================================
13748
13749 static void _DotLocalTestTimerHandler( void *inContext )
13750 {
13751 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
13752 }
13753
13754 //===========================================================================================================================
13755 // _DotLocalTestGAICallback
13756 //===========================================================================================================================
13757
13758 static void DNSSD_API
13759 _DotLocalTestGAICallback(
13760 DNSServiceRef inSDRef,
13761 DNSServiceFlags inFlags,
13762 uint32_t inInterfaceIndex,
13763 DNSServiceErrorType inError,
13764 const char * inHostname,
13765 const struct sockaddr * inSockAddr,
13766 uint32_t inTTL,
13767 void * inContext )
13768 {
13769 OSStatus err;
13770 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13771 DotLocalSubtest * const subtest = context->subtest;
13772 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
13773
13774 Unused( inSDRef );
13775 Unused( inInterfaceIndex );
13776 Unused( inHostname );
13777 Unused( inTTL );
13778
13779 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13780 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
13781
13782 if( context->state == kDotLocalTestState_GAINoSuchRecord )
13783 {
13784 if( inError == kDNSServiceErr_NoSuchRecord )
13785 {
13786 CFMutableArrayRef array = NULL;
13787 const char * noSuchRecordStr;
13788
13789 if( sip->sa.sa_family == AF_INET )
13790 {
13791 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13792 subtest->needDNSv4 = false;
13793
13794 noSuchRecordStr = kNoSuchRecordAStr;
13795 }
13796 else
13797 {
13798 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13799 subtest->needDNSv6 = false;
13800
13801 noSuchRecordStr = kNoSuchRecordAAAAStr;
13802 }
13803 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
13804 require_noerr( err, fatal );
13805 }
13806 else if( !inError )
13807 {
13808 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
13809 require_noerr( err, fatal );
13810 }
13811 else
13812 {
13813 err = inError;
13814 goto exit;
13815 }
13816 }
13817 else
13818 {
13819 if( !inError )
13820 {
13821 CFMutableArrayRef array = NULL;
13822
13823 if( sip->sa.sa_family == AF_INET )
13824 {
13825 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
13826
13827 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
13828 {
13829 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13830 subtest->needDNSv4 = false;
13831 }
13832 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
13833 {
13834 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13835 subtest->needMDNSv4 = false;
13836 }
13837 }
13838 else
13839 {
13840 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
13841
13842 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
13843 {
13844 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13845 subtest->needDNSv6 = false;
13846 }
13847 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
13848 {
13849 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13850 subtest->needMDNSv6 = false;
13851 }
13852 }
13853 if( !array ) array = subtest->unexpectedResults;
13854 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
13855 require_noerr( err, fatal );
13856 }
13857 else if( inError == kDNSServiceErr_NoSuchRecord )
13858 {
13859 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
13860 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
13861 require_noerr( err, fatal );
13862 }
13863 else
13864 {
13865 err = inError;
13866 goto exit;
13867 }
13868 }
13869
13870 exit:
13871 if( err )
13872 {
13873 subtest->error = err;
13874 _DotLocalTestStateMachine( context );
13875 }
13876 return;
13877
13878 fatal:
13879 ErrQuit( 1, "error: %#m\n", err );
13880 }
13881
13882 //===========================================================================================================================
13883 // _DotLocalTestQueryRecordCallback
13884 //===========================================================================================================================
13885
13886 static void DNSSD_API
13887 _DotLocalTestQueryRecordCallback(
13888 DNSServiceRef inSDRef,
13889 DNSServiceFlags inFlags,
13890 uint32_t inInterfaceIndex,
13891 DNSServiceErrorType inError,
13892 const char * inFullName,
13893 uint16_t inType,
13894 uint16_t inClass,
13895 uint16_t inRDataLen,
13896 const void * inRDataPtr,
13897 uint32_t inTTL,
13898 void * inContext )
13899 {
13900 OSStatus err;
13901 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13902 DotLocalSubtest * const subtest = context->subtest;
13903 const dns_fixed_fields_srv * fields;
13904 const uint8_t * target;
13905 const uint8_t * ptr;
13906 const uint8_t * end;
13907 char * rdataStr;
13908 unsigned int priority, weight, port;
13909 CFMutableArrayRef array;
13910
13911 Unused( inSDRef );
13912 Unused( inInterfaceIndex );
13913 Unused( inFullName );
13914 Unused( inTTL );
13915
13916 check( context->state == kDotLocalTestState_QuerySRV );
13917
13918 err = inError;
13919 require_noerr_quiet( err, exit );
13920 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13921 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
13922 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
13923
13924 fields = (const dns_fixed_fields_srv *) inRDataPtr;
13925 priority = dns_fixed_fields_srv_get_priority( fields );
13926 weight = dns_fixed_fields_srv_get_weight( fields );
13927 port = dns_fixed_fields_srv_get_port( fields );
13928 target = (const uint8_t *) &fields[ 1 ];
13929 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
13930 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
13931
13932 if( ( priority == kDotLocalTestSRV_Priority ) &&
13933 ( weight == kDotLocalTestSRV_Weight ) &&
13934 ( port == kDotLocalTestSRV_Port ) &&
13935 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
13936 {
13937 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
13938 subtest->needSRV = false;
13939 }
13940 else
13941 {
13942 array = subtest->unexpectedResults;
13943 }
13944
13945 rdataStr = NULL;
13946 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, NULL, 0, &rdataStr );
13947 if( !rdataStr )
13948 {
13949 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
13950 require_action( rdataStr, fatal, err = kNoMemoryErr );
13951 }
13952
13953 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
13954 free( rdataStr );
13955 require_noerr( err, fatal );
13956
13957 exit:
13958 if( err )
13959 {
13960 subtest->error = err;
13961 _DotLocalTestStateMachine( context );
13962 }
13963 return;
13964
13965 fatal:
13966 ErrQuit( 1, "error: %#m\n", err );
13967 }
13968
13969 //===========================================================================================================================
13970 // ProbeConflictTestCmd
13971 //===========================================================================================================================
13972
13973 #define kProbeConflictTestService_DefaultName "pctest-name"
13974 #define kProbeConflictTestService_Port 60000
13975
13976 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13977 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13978
13979 typedef struct
13980 {
13981 const char * description;
13982 const char * program;
13983 Boolean expectsRename;
13984
13985 } ProbeConflictTestCase;
13986
13987 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13988 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13989 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13990
13991 static const ProbeConflictTestCase kProbeConflictTestCases[] =
13992 {
13993 // No conflicts
13994
13995 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
13996
13997 // One multicast probe conflict
13998
13999 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
14000 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
14001 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
14002
14003 // One unicast probe conflict
14004
14005 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
14006 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
14007 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
14008
14009 // One multicast and one unicast probe conflict
14010
14011 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
14012 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
14013 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
14014 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
14015 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
14016 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14017 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
14018 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
14019 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14020
14021 // Two multicast probe conflicts
14022
14023 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
14024 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
14025 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
14026 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
14027 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
14028 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14029 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
14030 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
14031 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14032 };
14033
14034 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14035
14036 typedef struct
14037 {
14038 DNSServiceRef registration; // Test service registration.
14039 NanoTime64 testStartTime; // Test's start time.
14040 NanoTime64 startTime; // Current test case's start time.
14041 MDNSColliderRef collider; // mDNS collider object.
14042 CFMutableArrayRef results; // Array of test case results.
14043 char * serviceName; // Test service's instance name as a string. (malloced)
14044 char * serviceType; // Test service's service type as a string. (malloced)
14045 uint8_t * recordName; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14046 unsigned int testCaseIndex; // Index of the current test case.
14047 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
14048 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14049 OutputFormatType outputFormat; // Format of test report output.
14050 Boolean registered; // True if the test service instance is currently registered.
14051 Boolean testFailed; // True if at least one test case failed.
14052
14053 } ProbeConflictTestContext;
14054
14055 static void DNSSD_API
14056 _ProbeConflictTestRegisterCallback(
14057 DNSServiceRef inSDRef,
14058 DNSServiceFlags inFlags,
14059 DNSServiceErrorType inError,
14060 const char * inName,
14061 const char * inType,
14062 const char * inDomain,
14063 void * inContext );
14064 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
14065 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
14066 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
14067 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
14068
14069 static void ProbeConflictTestCmd( void )
14070 {
14071 OSStatus err;
14072 ProbeConflictTestContext * context;
14073 const char * serviceName;
14074 char tag[ 6 + 1 ];
14075
14076 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
14077 require_action( context, exit, err = kNoMemoryErr );
14078
14079 if( gProbeConflictTest_Interface )
14080 {
14081 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
14082 require_noerr_quiet( err, exit );
14083 }
14084 else
14085 {
14086 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14087 require_noerr_quiet( err, exit );
14088 }
14089
14090 if( gProbeConflictTest_OutputFilePath )
14091 {
14092 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
14093 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
14094 }
14095
14096 err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
14097 require_noerr_quiet( err, exit );
14098
14099 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
14100 require_action( context->results, exit, err = kNoMemoryErr );
14101
14102 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
14103
14104 ASPrintF( &context->serviceType, "_pctest-%s._udp",
14105 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14106 require_action( context->serviceType, exit, err = kNoMemoryErr );
14107
14108 context->testStartTime = NanoTimeGetCurrent();
14109 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
14110 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
14111 require_noerr( err, exit );
14112
14113 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
14114 require_noerr( err, exit );
14115
14116 dispatch_main();
14117
14118 exit:
14119 exit( 1 );
14120 }
14121
14122 //===========================================================================================================================
14123 // _ProbeConflictTestRegisterCallback
14124 //===========================================================================================================================
14125
14126 static void DNSSD_API
14127 _ProbeConflictTestRegisterCallback(
14128 DNSServiceRef inSDRef,
14129 DNSServiceFlags inFlags,
14130 DNSServiceErrorType inError,
14131 const char * inName,
14132 const char * inType,
14133 const char * inDomain,
14134 void * inContext )
14135 {
14136 OSStatus err;
14137 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14138
14139 Unused( inSDRef );
14140 Unused( inType );
14141 Unused( inDomain );
14142
14143 err = inError;
14144 require_noerr( err, exit );
14145
14146 if( !context->registered )
14147 {
14148 if( inFlags & kDNSServiceFlagsAdd )
14149 {
14150 uint8_t * ptr;
14151 size_t recordNameLen;
14152 unsigned int len;
14153 uint8_t name[ kDomainNameLengthMax ];
14154
14155 context->registered = true;
14156
14157 FreeNullSafe( context->serviceName );
14158 context->serviceName = strdup( inName );
14159 require_action( context->serviceName, exit, err = kNoMemoryErr );
14160
14161 err = DomainNameFromString( name, context->serviceName, NULL );
14162 require_noerr( err, exit );
14163
14164 err = DomainNameAppendString( name, context->serviceType, NULL );
14165 require_noerr( err, exit );
14166
14167 err = DomainNameAppendString( name, "local", NULL );
14168 require_noerr( err, exit );
14169
14170 ForgetMem( &context->recordName );
14171 err = DomainNameDup( name, &context->recordName, &recordNameLen );
14172 require_noerr( err, exit );
14173 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14174
14175 // Make the first label all caps so that it's easier to spot in system logs.
14176
14177 ptr = context->recordName;
14178 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
14179
14180 err = _ProbeConflictTestStartNextTest( context );
14181 require_noerr( err, exit );
14182 }
14183 }
14184 else
14185 {
14186 if( !( inFlags & kDNSServiceFlagsAdd ) )
14187 {
14188 context->registered = false;
14189 err = _ProbeConflictTestStopCurrentTest( context, true );
14190 require_noerr( err, exit );
14191 }
14192 }
14193 err = kNoErr;
14194
14195 exit:
14196 if( err ) exit( 1 );
14197 }
14198
14199 //===========================================================================================================================
14200 // _ProbeConflictTestColliderStopHandler
14201 //===========================================================================================================================
14202
14203 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
14204 {
14205 OSStatus err;
14206 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14207
14208 err = inError;
14209 require_noerr_quiet( err, exit );
14210
14211 ForgetCF( &context->collider );
14212
14213 err = _ProbeConflictTestStopCurrentTest( context, false );
14214 require_noerr( err, exit );
14215
14216 err = _ProbeConflictTestStartNextTest( context );
14217 require_noerr( err, exit );
14218
14219 exit:
14220 if( err ) exit( 1 );
14221 }
14222
14223 //===========================================================================================================================
14224 // _ProbeConflictTestStartNextTest
14225 //===========================================================================================================================
14226
14227 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
14228 {
14229 OSStatus err;
14230 const ProbeConflictTestCase * testCase;
14231
14232 check( !inContext->collider );
14233
14234 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
14235 {
14236 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14237 }
14238 else
14239 {
14240 _ProbeConflictTestFinalizeAndExit( inContext );
14241 }
14242
14243 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
14244 require_noerr( err, exit );
14245
14246 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
14247 require_noerr( err, exit );
14248
14249 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
14250 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
14251 require_noerr( err, exit );
14252
14253 MDNSColliderSetProtocols( inContext->collider, kMDNSColliderProtocol_IPv4 );
14254 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
14255 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
14256
14257 inContext->startTime = NanoTimeGetCurrent();
14258 err = MDNSColliderStart( inContext->collider );
14259 require_noerr( err, exit );
14260
14261 exit:
14262 return( err );
14263 }
14264
14265 //===========================================================================================================================
14266 // _ProbeConflictTestStopCurrentTest
14267 //===========================================================================================================================
14268
14269 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14270 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14271 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14272 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14273 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14274 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14275
14276 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
14277 {
14278 OSStatus err;
14279 const ProbeConflictTestCase * testCase;
14280 NanoTime64 now;
14281 Boolean passed;
14282 char startTime[ 32 ];
14283 char endTime[ 32 ];
14284
14285 now = NanoTimeGetCurrent();
14286
14287 if( inContext->collider )
14288 {
14289 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
14290 MDNSColliderStop( inContext->collider );
14291 CFRelease( inContext->collider );
14292 inContext->collider = NULL;
14293 }
14294
14295 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14296 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
14297 if( !passed ) inContext->testFailed = true;
14298
14299 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
14300 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14301
14302 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
14303 "{"
14304 "%kO=%s" // description
14305 "%kO=%b" // expectedRename
14306 "%kO=%s" // startTime
14307 "%kO=%s" // endTime
14308 "%kO=%s" // serviceName
14309 "%kO=%b" // passed
14310 "}",
14311 kProbeConflictTestCaseResultKey_Description, testCase->description,
14312 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
14313 kProbeConflictTestCaseResultKey_StartTime, startTime,
14314 kProbeConflictTestCaseResultKey_EndTime, endTime,
14315 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
14316 kProbeConflictTestCaseResultKey_Passed, passed );
14317 require_noerr( err, exit );
14318
14319 ++inContext->testCaseIndex;
14320
14321 exit:
14322 return( err );
14323 }
14324
14325 //===========================================================================================================================
14326 // _ProbeConflictTestFinalizeAndExit
14327 //===========================================================================================================================
14328
14329 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14330 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14331 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14332 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14333 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14334
14335 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
14336 {
14337 OSStatus err;
14338 CFPropertyListRef plist;
14339 NanoTime64 now;
14340 char startTime[ 32 ];
14341 char endTime[ 32 ];
14342
14343 now = NanoTimeGetCurrent();
14344
14345 check( !inContext->collider );
14346
14347 _NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
14348 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
14349
14350 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
14351 "{"
14352 "%kO=%s" // startTime
14353 "%kO=%s" // endTime
14354 "%kO=%s" // serviceType
14355 "%kO=%O" // results
14356 "%kO=%b" // passed
14357 "}",
14358 kProbeConflictTestReportKey_StartTime, startTime,
14359 kProbeConflictTestReportKey_EndTime, endTime,
14360 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
14361 kProbeConflictTestReportKey_Results, inContext->results,
14362 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
14363 require_noerr( err, exit );
14364 ForgetCF( &inContext->results );
14365
14366 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
14367 CFRelease( plist );
14368 require_noerr( err, exit );
14369
14370 exit( inContext->testFailed ? 2 : 0 );
14371
14372 exit:
14373 ErrQuit( 1, "error: %#m\n", err );
14374 }
14375
14376 //===========================================================================================================================
14377 // ExpensiveConstrainedsTestCmd
14378 //===========================================================================================================================
14379
14380 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14381 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14382 #define LOOPBACK_INTERFACE_NAME "lo0"
14383 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14384 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14385 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14386 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14387 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14388
14389 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14390 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14391 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14392 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14393
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14396 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14397 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14402 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14403 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14404
14405 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14406 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14407 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14408 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14409 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14410 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14411 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14412 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14413
14414 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14415 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14416 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14417 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14418 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14419
14420 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14421 enum ExpensiveConstrainedTestState
14422 {
14423 TEST_BEGIN,
14424 TEST_EXPENSIVE_PREPARE,
14425 TEST_EXPENSIVE, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14426 TEST_CONSTRAINED_PREPARE,
14427 TEST_CONSTRAINED, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14428 TEST_EXPENSIVE_CONSTRAINED_PREPARE,
14429 TEST_EXPENSIVE_CONSTRAINED, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14430 TEST_FAILED,
14431 TEST_SUCCEEDED
14432 };
14433 enum ExpensiveConstrainedTestOperation
14434 {
14435 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.
14436 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.
14437 NO_UPDATE // no status update notification
14438 };
14439
14440 typedef struct
14441 {
14442 uint32_t subtestIndex; // The index of parameter for the subtest
14443 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
14444 const char * name; // Hostname to resolve.
14445 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
14446 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
14447 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
14448 char ifName[IFNAMSIZ]; // Interface name for the given interface index.
14449 dispatch_source_t timer; // The test will check if the current behavior is valid, which is called by
14450 // the timer per 2s.
14451 pid_t serverPID;
14452 Boolean isExpensivePrev; // If the interface is expensive in the previous test step.
14453 Boolean isExpensiveNow; // If the interface is expensive now.
14454 Boolean isConstrainedPrev; // If the interface is constrained in the previous test step.
14455 Boolean isConstrainedNow; // If the interface is constrained now.
14456 Boolean startFromExpensive; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14457 uint8_t numOfRetries; // the number of retries we can have if the test fail
14458 struct timeval updateTime; // The time when interface status(expensive or constrained) is changed.
14459 struct timeval notificationTime; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14460 uint32_t counter; // To record how many times the test has repeated.
14461 enum ExpensiveConstrainedTestState state; // The current test state.
14462 enum ExpensiveConstrainedTestOperation expectedOperation; // the test expects this kind of notification
14463 enum ExpensiveConstrainedTestOperation operation; // represents what notification the callback function gets.
14464
14465 NanoTime64 testReport_startTime; // when the entire test starts
14466 CFMutableArrayRef subtestReport; // stores the log message for every subtest
14467 NanoTime64 subtestReport_startTime; // when the subtest starts
14468 CFMutableArrayRef subtestProgress; // one test iteration
14469 NanoTime64 subtestProgress_startTime; // when the test iteration starts
14470 CFMutableArrayRef subtestProgress_callBack; // array of ADD/REMOVE events
14471 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14472 OutputFormatType outputFormat; // Format of test report output.
14473 } ExpensiveConstrainedContext;
14474
14475 // structure that controls how the subtest is run
14476 typedef struct
14477 {
14478 const char *qname; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14479 Boolean deny_expensive; // if the query should avoid using expensive interface
14480 Boolean deny_constrained; // if the query should avoid using constrained interface
14481 Boolean start_from_expensive; // if the query should starts from using an expensive interface
14482 Boolean ipv4_query; // only allow IPv4 query
14483 Boolean ipv6_query; // only allow IPv6 query
14484 int8_t test_passed; // if the subtest passes
14485 } ExpensiveConstrainedTestParams;
14486
14487 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams[] =
14488 {
14489 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14493 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14494 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14495 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14496 // IPv4 Only
14497 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14498 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14499 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14500 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14501 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14502 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14503 // IPv6 Only
14504 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14505 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14506 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14507 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14508 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14509 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14510 };
14511
14512 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
14513 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
14514 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
14515 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
14516 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
14517 static void DNSSD_API
14518 ExpensiveConstrainedCallback(
14519 DNSServiceRef inSDRef,
14520 DNSServiceFlags inFlags,
14521 uint32_t inInterfaceIndex,
14522 DNSServiceErrorType inError,
14523 const char * inHostname,
14524 const struct sockaddr * inSockAddr,
14525 uint32_t inTTL,
14526 void * inContext );
14527 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
14528 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
14529 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
14530 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
14531 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
14532 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
14533 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
14534 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
14535 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
14536
14537 //===========================================================================================================================
14538 // ExpensiveConstrainedTestCmd
14539 //===========================================================================================================================
14540
14541 static void ExpensiveConstrainedTestCmd( void )
14542 {
14543 OSStatus err;
14544 dispatch_source_t signalSource = NULL;
14545 ExpensiveConstrainedContext * context = NULL;
14546
14547 // Set up SIGINT handler.
14548 signal( SIGINT, SIG_IGN );
14549 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
14550 require_noerr( err, exit );
14551 dispatch_resume( signalSource );
14552
14553 // create the test context
14554 context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
14555 require_action( context, exit, err = kNoMemoryErr );
14556
14557 // get the command line option
14558 err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
14559 require_noerr_quiet( err, exit );
14560 if ( gExpensiveConstrainedTest_OutputFilePath )
14561 {
14562 context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
14563 require_noerr_quiet( context->outputFilePath, exit );
14564 }
14565
14566 // initialize context
14567 context->subtestIndex = 0;
14568 context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
14569
14570 // initialize the CFArray used to store the log
14571 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14572 context->testReport_startTime = NanoTimeGetCurrent();
14573
14574 // setup local DNS server
14575 ExpensiveConstrainedSetupLocalDNSServer( context );
14576
14577 ExpensiveConstrainedStartTestHandler( context );
14578
14579 dispatch_main();
14580
14581 exit:
14582 exit( 1 );
14583 }
14584
14585 //===========================================================================================================================
14586 // ExpensiveConstrainedSetupLocalDNSServer
14587 //===========================================================================================================================
14588
14589 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
14590 {
14591 pid_t current_pid = getpid();
14592 OSStatus err = SpawnCommand( &context->serverPID, "dnssdutil server -l --follow %d", current_pid );
14593 if (err != 0)
14594 {
14595 FPrintF( stdout, "dnssdutil server -l --follow <PID> failed, error: %d\n", err );
14596 exit( 1 );
14597 }
14598 sleep(2);
14599 }
14600
14601 //===========================================================================================================================
14602 // ExpensiveConstrainedStartTestHandler
14603 //===========================================================================================================================
14604
14605 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
14606 {
14607 // setup 3s timer
14608 ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
14609
14610 // set the event handler for the 3s timer
14611 dispatch_source_set_event_handler( context->timer, ^{
14612 ExpensiveConstrainedTestTimerEventHandler( context );
14613 } );
14614
14615 dispatch_resume( context->timer );
14616 }
14617
14618 //===========================================================================================================================
14619 // ExpensiveConstrainedStartTestHandler
14620 //===========================================================================================================================
14621
14622 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
14623 {
14624 dispatch_cancel( context->timer );
14625 dispatch_release( context->timer );
14626 context->timer = NULL;
14627 }
14628
14629 //===========================================================================================================================
14630 // ExpensiveConstrainedSetupTimer
14631 //===========================================================================================================================
14632
14633 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
14634 {
14635 // set the timer source, the event handler will be called for every "second" seconds
14636 context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
14637 if ( context->timer == NULL )
14638 {
14639 FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14640 exit( 1 );
14641 }
14642 // the first block will be put into the queue "second"s after calling dispatch_resume
14643 dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
14644 (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
14645 }
14646
14647 //===========================================================================================================================
14648 // ExpensiveConstrainedTestTimerEventHandler
14649 //===========================================================================================================================
14650
14651 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
14652 {
14653 OSStatus err;
14654 char buffer[ 1024 ];
14655 const char *errorDescription = NULL;
14656
14657 // do not log the state if we are in transition state
14658 if (context->state != TEST_BEGIN
14659 && context->state != TEST_SUCCEEDED
14660 && context->state != TEST_CONSTRAINED_PREPARE
14661 && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
14662 ExpensiveConstrainedSubtestProgressReport( context );
14663
14664 switch ( context->state ) {
14665 case TEST_BEGIN:
14666 {
14667 ExpensiveConstrainedStopTestHandler( context );
14668
14669 // clear mDNSResponder cache
14670 err = systemf( NULL, "killall -HUP mDNSResponder" );
14671 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14672
14673 // initialize the global parameters
14674 ExpensiveConstrainedInitializeContext( context );
14675
14676 // The local DNS server is set up on the local only interface.
14677 gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
14678 strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
14679
14680 // The local DNS server is unscoped, so we must set our question to unscoped.
14681 context->ifIndex = kDNSServiceInterfaceIndexAny;
14682
14683 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14684 // use the local dns server set up previously to run the test locally.
14685 require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
14686 SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
14687 errorDescription = buffer );
14688
14689 // get the quesion name
14690 context->name = gExpensiveConstrainedTest_Name;
14691
14692 // set the initial state for the interface
14693 context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
14694 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
14695 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
14696 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14697
14698 // get question flag
14699 if ( gExpensiveConstrainedTest_DenyExpensive ) context->flags |= kDNSServiceFlagsDenyExpensive;
14700 if ( gExpensiveConstrainedTest_DenyConstrained ) context->flags |= kDNSServiceFlagsDenyConstrained;
14701 if ( gExpensiveConstrainedTest_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
14702 if ( gExpensiveConstrainedTest_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
14703
14704 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14705 #if( TARGET_OS_WATCH )
14706 context->flags |= kDNSServiceFlagsPathEvaluationDone;
14707 #endif
14708
14709 // start the query
14710 DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
14711
14712 // set the initial test status
14713 context->subtestReport_startTime = NanoTimeGetCurrent();
14714 context->subtestProgress_startTime = NanoTimeGetCurrent();
14715 context->state = TEST_EXPENSIVE_PREPARE; // start from expensive test
14716 context->isExpensiveNow = context->startFromExpensive ? true : false;
14717 context->isConstrainedNow = false;
14718 context->expectedOperation = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
14719 context->operation = NO_UPDATE;
14720 context->subtestProgress = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14721 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14722 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
14723 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14724
14725 // set the queue where the callback will be called when there is an answer for the query
14726 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
14727 require_noerr( err, test_failed );
14728
14729 ExpensiveConstrainedStartTestHandler( context );
14730 }
14731 break;
14732 case TEST_EXPENSIVE_PREPARE:
14733 require_action( context->isConstrainedNow == false, test_failed,
14734 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14735 errorDescription = buffer );
14736 require_action( context->expectedOperation == context->operation, test_failed,
14737 errorDescription = "Operation is not expected" );
14738
14739 context->subtestProgress_startTime = NanoTimeGetCurrent();
14740 context->state = TEST_EXPENSIVE; // begin to test expensive flag
14741 context->counter = 0; // the number of test repetition that has passed
14742 context->isExpensivePrev = context->isExpensiveNow;
14743 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14744 context->isConstrainedPrev = false; // the interface is currently unconstrained
14745 context->isConstrainedNow = false; // the interface will be unconstrained in the current test
14746 if ( gExpensiveConstrainedTest_DenyExpensive )
14747 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14748 else
14749 context->expectedOperation = NO_UPDATE;
14750 context->operation = NO_UPDATE; // NO_UPDATE means the call back function has not been called
14751 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14752 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14753
14754 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14755 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14756
14757 // record the starting timestamp
14758 gettimeofday( &context->updateTime, NULL );
14759
14760 break;
14761 case TEST_EXPENSIVE:
14762 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14763 require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
14764 SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
14765 errorDescription = buffer );
14766 // constrained flag is always turned off when testing expensive
14767 require_action( context->isConstrainedNow == false, test_failed,
14768 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
14769 errorDescription = buffer );
14770 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14771
14772 context->counter++; // one test repetition has passed
14773 if ( context->counter == TEST_REPETITION ) // expensive test finished
14774 {
14775 // prepare to test constrained flag
14776 context->state = TEST_CONSTRAINED_PREPARE;
14777
14778 // reset the interface
14779 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14780 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14781
14782 context->isExpensiveNow = false;
14783 context->isConstrainedNow = false;
14784 gettimeofday( &context->updateTime, NULL );
14785 }
14786 else
14787 {
14788 context->subtestProgress_startTime = NanoTimeGetCurrent();
14789 context->isExpensivePrev = context->isExpensiveNow;
14790 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
14791 if ( gExpensiveConstrainedTest_DenyExpensive )
14792 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14793 else
14794 context->expectedOperation = NO_UPDATE;
14795 context->operation = NO_UPDATE;
14796 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14797 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14798
14799 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
14800 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14801
14802 gettimeofday( &context->updateTime, NULL );
14803 }
14804 break;
14805 case TEST_CONSTRAINED_PREPARE:
14806 // The interface should be inexpensive and unconstrained when the constrained test starts
14807 require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
14808 errorDescription = buffer );
14809 require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14810 errorDescription = buffer );
14811
14812 context->subtestProgress_startTime = NanoTimeGetCurrent();
14813 context->state = TEST_CONSTRAINED; // constrained interface is now under testing
14814 context->counter = 0;
14815 context->isExpensivePrev = false;
14816 context->isExpensiveNow = false;
14817 context->isConstrainedPrev = false;
14818 context->isConstrainedNow = true; // will set constrained flag on the interface
14819 if ( gExpensiveConstrainedTest_DenyConstrained )
14820 context->expectedOperation = RESULT_RMV;
14821 else
14822 context->expectedOperation = NO_UPDATE;
14823 context->operation = NO_UPDATE;
14824 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14825 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14826
14827 // change interface to the constrained one
14828 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
14829 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14830
14831 gettimeofday( &context->updateTime, NULL );
14832 break;
14833 case TEST_CONSTRAINED:
14834 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14835 require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
14836 SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
14837 errorDescription = buffer );
14838 require_action( context->isExpensiveNow == false, test_failed,
14839 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
14840 errorDescription = buffer );
14841 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
14842
14843 context->counter++;
14844 if (context->counter == TEST_REPETITION)
14845 {
14846 // test changing expensive and constrained flags at the same time
14847 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
14848
14849 // reset interface
14850 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
14851 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14852
14853 context->isExpensiveNow = false;
14854 context->isConstrainedNow = false;
14855 gettimeofday( &context->updateTime, NULL );
14856 }
14857 else
14858 {
14859 context->subtestProgress_startTime = NanoTimeGetCurrent();
14860 context->isConstrainedPrev = context->isConstrainedNow;
14861 context->isConstrainedNow = !context->isConstrainedNow; // flip constrained flag
14862 if ( gExpensiveConstrainedTest_DenyConstrained )
14863 context->expectedOperation = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
14864 else
14865 context->expectedOperation = NO_UPDATE;
14866 context->operation = NO_UPDATE;
14867 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14868 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14869
14870 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
14871 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14872
14873 gettimeofday(&context->updateTime, NULL);
14874 }
14875 break;
14876 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
14877 // The interface should be inexpensive and unconstrained when the constrained test starts
14878 require_action( context->isExpensiveNow == false, test_failed,
14879 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
14880 errorDescription = buffer );
14881 require_action( context->isConstrainedNow == false, test_failed,
14882 SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
14883 errorDescription = buffer );
14884
14885 // now flip expensive and constrained at the same time
14886 context->subtestProgress_startTime = NanoTimeGetCurrent();
14887 context->state = TEST_EXPENSIVE_CONSTRAINED;
14888 context->counter = 0;
14889 context->isExpensivePrev = false;
14890 context->isExpensiveNow = true;
14891 context->isConstrainedPrev = false;
14892 context->isConstrainedNow = true;
14893 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14894 context->expectedOperation = RESULT_RMV;
14895 else
14896 context->expectedOperation = NO_UPDATE;
14897 context->operation = NO_UPDATE;
14898 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14899 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14900
14901 err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
14902 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14903
14904 gettimeofday( &context->updateTime, NULL );
14905 break;
14906 case TEST_EXPENSIVE_CONSTRAINED:
14907 // expensive and constrained flag should always be changed
14908 require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
14909 SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
14910 errorDescription = buffer );
14911 require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
14912 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
14913
14914 context->counter++;
14915 if ( context->counter == TEST_REPETITION )
14916 {
14917 context->state = TEST_SUCCEEDED;
14918 }
14919 else
14920 {
14921 context->subtestProgress_startTime = NanoTimeGetCurrent();
14922 context->isExpensivePrev = context->isExpensiveNow;
14923 context->isExpensiveNow = !context->isExpensiveNow;
14924 context->isConstrainedPrev = context->isConstrainedNow;
14925 context->isConstrainedNow = !context->isConstrainedNow;
14926 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
14927 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
14928 else
14929 context->expectedOperation = NO_UPDATE;
14930 context->operation = NO_UPDATE;
14931 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
14932 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
14933
14934 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
14935 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
14936
14937 gettimeofday( &context->updateTime, NULL );
14938 }
14939 break;
14940 case TEST_FAILED:
14941 test_failed:
14942 ExpensiveConstrainedSubtestReport( context, errorDescription );
14943 ExpensiveConstrainedStopAndCleanTheTest( context );
14944 if ( context->numOfRetries > 0 )
14945 {
14946 context->state = TEST_BEGIN;
14947 context->numOfRetries--;
14948 break;
14949 }
14950 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
14951 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14952 {
14953 ExpensiveConstrainedFinalResultReport( context, false );
14954 exit( 2 );
14955 }
14956 if (context->timer == NULL)
14957 {
14958 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14959 ExpensiveConstrainedFinalResultReport( context, false );
14960 exit( 1 );
14961 }
14962 context->state = TEST_BEGIN;
14963 break;
14964 case TEST_SUCCEEDED:
14965 ExpensiveConstrainedSubtestReport( context, NULL );
14966 ExpensiveConstrainedStopAndCleanTheTest( context );
14967 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
14968 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
14969 {
14970 // all the subtests have been run
14971 Boolean hasFailed = false;
14972 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
14973 hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
14974
14975 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
14976 exit( hasFailed ? 2 : 0 );
14977 }
14978 context->state = TEST_BEGIN;
14979 break;
14980 default:
14981 FPrintF( stdout, "unknown error\n" );
14982 exit( 1 );
14983 }
14984 }
14985
14986 //===========================================================================================================================
14987 // ExpensiveConstrainedCallback
14988 //===========================================================================================================================
14989
14990 static void DNSSD_API
14991 ExpensiveConstrainedCallback(
14992 __unused DNSServiceRef inSDRef,
14993 DNSServiceFlags inFlags,
14994 uint32_t inInterfaceIndex,
14995 DNSServiceErrorType inError,
14996 const char * inHostname,
14997 const struct sockaddr * inSockAddr,
14998 __unused uint32_t inTTL,
14999 void * inContext )
15000 {
15001 ExpensiveConstrainedContext * const context = (ExpensiveConstrainedContext *)inContext;
15002 OSStatus err;
15003 const char * addrStr;
15004 char addrStrBuf[ kSockAddrStringMaxSize ];
15005 char inFlagsDescription[ 128 ];
15006 NanoTime64 now;
15007 char nowTimestamp[ 32 ];
15008
15009 switch ( inError ) {
15010 case kDNSServiceErr_NoError:
15011 case kDNSServiceErr_NoSuchRecord:
15012 break;
15013
15014 case kDNSServiceErr_Timeout:
15015 Exit( kExitReason_Timeout );
15016
15017 default:
15018 err = inError;
15019 goto exit;
15020 }
15021
15022 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
15023 {
15024 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
15025 err = kTypeErr;
15026 goto exit;
15027 }
15028
15029 if( !inError )
15030 {
15031 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
15032 require_noerr( err, exit );
15033 addrStr = addrStrBuf;
15034 }
15035 else
15036 {
15037 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
15038 }
15039
15040 now = NanoTimeGetCurrent();
15041 _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
15042 SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
15043 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress_callBack,
15044 "{"
15045 "%kO=%s"
15046 "%kO=%s"
15047 "%kO=%s"
15048 "%kO=%lli"
15049 "%kO=%s"
15050 "}",
15051 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP, nowTimestamp,
15052 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME, inHostname,
15053 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS, inFlagsDescription,
15054 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE, (int64_t) inInterfaceIndex,
15055 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS, addrStr
15056 );
15057 require_noerr_quiet( err, exit );
15058
15059 if ( inFlags & kDNSServiceFlagsMoreComing )
15060 return;
15061
15062 if ( inFlags & kDNSServiceFlagsAdd )
15063 context->operation = RESULT_ADD;
15064 else
15065 context->operation = RESULT_RMV;
15066
15067 gettimeofday(&context->notificationTime, NULL);
15068 exit:
15069 if( err ) exit( 1 );
15070 }
15071
15072 //===========================================================================================================================
15073 // ExpensiveConstrainedInitializeContext
15074 //===========================================================================================================================
15075
15076 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
15077 {
15078 // clear the flags of the previous subtest
15079 context->flags = 0;
15080 context->protocols = 0;
15081
15082 // get the parameter for the current subtest
15083 const ExpensiveConstrainedTestParams *param = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
15084 gExpensiveConstrainedTest_Name = param->qname;
15085 gExpensiveConstrainedTest_DenyExpensive = param->deny_expensive;
15086 gExpensiveConstrainedTest_DenyConstrained = param->deny_constrained;
15087 gExpensiveConstrainedTest_StartFromExpensive = param->start_from_expensive;
15088 gExpensiveConstrainedTest_ProtocolIPv4 = param->ipv4_query;
15089 gExpensiveConstrainedTest_ProtocolIPv6 = param->ipv6_query;
15090 }
15091
15092 //===========================================================================================================================
15093 // ExpensiveConstrainedStopAndCleanTheTest
15094 //===========================================================================================================================
15095
15096 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
15097 {
15098 // Stop the ongoing query
15099 if ( context->opRef != NULL )
15100 DNSServiceRefDeallocate( context->opRef );
15101
15102 context->opRef = NULL;
15103 context->flags = 0;
15104 context->protocols = 0;
15105 }
15106
15107 //===========================================================================================================================
15108 // ExpensiveConstrainedSubtestProgressReport
15109 //===========================================================================================================================
15110
15111 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
15112 {
15113 OSStatus err;
15114 NanoTime64 now;
15115 char startTime[ 32 ];
15116 char endTime[ 32 ];
15117 char expensive[ 32 ];
15118 char constrained[ 32 ];
15119
15120 now = NanoTimeGetCurrent();
15121 _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
15122 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15123
15124 snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
15125 snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
15126
15127 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
15128 "{"
15129 "%kO=%s"
15130 "%kO=%s"
15131 "%kO=%s"
15132 "%kO=%s"
15133 "%kO=%s"
15134 "%kO=%s"
15135 "%kO=%s"
15136 "%kO=%O"
15137 "}",
15138 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME, startTime,
15139 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME, endTime,
15140 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE, ExpensiveConstrainedStateString(context->state),
15141 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT, ExpensiveConstrainedOperationString(context->expectedOperation),
15142 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT, ExpensiveConstrainedOperationString(context->operation),
15143 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW, expensive,
15144 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW, constrained,
15145 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK, context->subtestProgress_callBack
15146 );
15147 require_noerr( err, exit );
15148 ForgetCF( &context->subtestProgress_callBack );
15149 return;
15150
15151 exit:
15152 ErrQuit( 1, "error: %#m\n", err );
15153 }
15154
15155 //===========================================================================================================================
15156 // ExpensiveConstrainedFinalSubtestReport
15157 //===========================================================================================================================
15158
15159 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
15160 {
15161 OSStatus err;
15162 NanoTime64 now;
15163 char startTime[ 32 ];
15164 char endTime[ 32 ];
15165 char flagDescription[ 1024 ];
15166
15167 now = NanoTimeGetCurrent();
15168 _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
15169 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15170 SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
15171
15172 if (error_description != NULL)
15173 {
15174 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15175 "{"
15176 "%kO=%s"
15177 "%kO=%s"
15178 "%kO=%s"
15179 "%kO=%s"
15180 "%kO=%s"
15181 "%kO=%lli"
15182 "%kO=%s"
15183 "%kO=%O"
15184 "%kO=%s"
15185 "%kO=%O"
15186 "}",
15187 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15188 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15189 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15190 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15191 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15192 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15193 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15194 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Fail" ),
15195 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR, error_description,
15196 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15197 );
15198 }
15199 else
15200 {
15201 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
15202 "{"
15203 "%kO=%s"
15204 "%kO=%s"
15205 "%kO=%s"
15206 "%kO=%s"
15207 "%kO=%s"
15208 "%kO=%lli"
15209 "%kO=%s"
15210 "%kO=%O"
15211 "%kO=%O"
15212 "}",
15213 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
15214 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
15215 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
15216 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
15217 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
15218 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
15219 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
15220 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Pass" ),
15221 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
15222 );
15223 }
15224
15225 require_noerr( err, exit );
15226 ForgetCF( &context->subtestProgress );
15227 return;
15228 exit:
15229 ErrQuit( 1, "error: %#m\n", err );
15230 }
15231
15232 //===========================================================================================================================
15233 // ExpensiveConstrainedFinalResultReport
15234 //===========================================================================================================================
15235
15236 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
15237 {
15238 OSStatus err;
15239 CFPropertyListRef plist;
15240 NanoTime64 now;
15241 char startTime[ 32 ];
15242 char endTime[ 32 ];
15243
15244 now = NanoTimeGetCurrent();
15245 _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
15246 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
15247
15248 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15249 "{"
15250 "%kO=%s"
15251 "%kO=%s"
15252 "%kO=%b"
15253 "%kO=%O"
15254 "}",
15255 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME, startTime,
15256 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME, endTime,
15257 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED, allPassed,
15258 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT, context->subtestReport
15259 );
15260 require_noerr( err, exit );
15261 ForgetCF( &context->subtestReport );
15262
15263 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15264 CFRelease( plist );
15265 require_noerr( err, exit );
15266
15267 return;
15268 exit:
15269 ErrQuit( 1, "error: %#m\n", err );
15270 }
15271
15272 //===========================================================================================================================
15273 // ExpensiveConstrainedProtocolString
15274 //===========================================================================================================================
15275
15276 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
15277 {
15278 const char *str = NULL;
15279 switch ( protocol ) {
15280 case kDNSServiceProtocol_IPv4:
15281 str = "IPv4";
15282 break;
15283 case kDNSServiceProtocol_IPv6:
15284 str = "IPv6";
15285 break;
15286 case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
15287 str = "IPv4 & IPv6";
15288 break;
15289 default:
15290 break;
15291 }
15292 return str;
15293 }
15294
15295 //===========================================================================================================================
15296 // ExpensiveConstrainedStateString
15297 //===========================================================================================================================
15298
15299 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
15300 {
15301 const char *str = NULL;
15302 switch ( state ) {
15303 case TEST_BEGIN:
15304 str = "TEST_BEGIN";
15305 break;
15306 case TEST_EXPENSIVE_PREPARE:
15307 str = "TEST_EXPENSIVE_PREPARE";
15308 break;
15309 case TEST_EXPENSIVE:
15310 str = "TEST_EXPENSIVE";
15311 break;
15312 case TEST_CONSTRAINED_PREPARE:
15313 str = "TEST_CONSTRAINED_PREPARE";
15314 break;
15315 case TEST_CONSTRAINED:
15316 str = "TEST_CONSTRAINED";
15317 break;
15318 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
15319 str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15320 break;
15321 case TEST_EXPENSIVE_CONSTRAINED:
15322 str = "TEST_EXPENSIVE_CONSTRAINED";
15323 break;
15324 case TEST_FAILED:
15325 str = "TEST_FAILED";
15326 break;
15327 case TEST_SUCCEEDED:
15328 str = "TEST_SUCCEEDED";
15329 break;
15330 default:
15331 str = "UNKNOWN";
15332 break;
15333 }
15334
15335 return str;
15336 }
15337
15338 //===========================================================================================================================
15339 // ExpensiveConstrainedOperationString
15340 //===========================================================================================================================
15341
15342 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
15343 {
15344 const char *str = NULL;
15345 switch ( operation ) {
15346 case RESULT_ADD:
15347 str = "RESULT_ADD";
15348 break;
15349 case RESULT_RMV:
15350 str = "RESULT_RMV";
15351 break;
15352 case NO_UPDATE:
15353 str = "NO_UPDATE";
15354 break;
15355 default:
15356 str = "UNKNOWN";
15357 break;
15358 }
15359 return str;
15360 }
15361
15362 //===========================================================================================================================
15363 // expensiveConstrainedEndsWith
15364 //===========================================================================================================================
15365 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
15366 {
15367 if ( !str || !suffix )
15368 return false;
15369 size_t lenstr = strlen( str );
15370 size_t lensuffix = strlen( suffix );
15371 if ( lensuffix > lenstr )
15372 return false;
15373 return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
15374 }
15375
15376 //===========================================================================================================================
15377 // RegistrationTestCmd
15378 //===========================================================================================================================
15379
15380 typedef struct RegistrationSubtest RegistrationSubtest;
15381
15382 typedef struct
15383 {
15384 CFMutableArrayRef subtestReports; // Array of subtest reports.
15385 dispatch_source_t timer; // Timer to enforce subtest durations.
15386 dispatch_source_t sigSourceINT; // SIGINT signal handler for a clean test exit.
15387 dispatch_source_t sigSourceTERM; // SIGTERM signal handler for a clean test exit.
15388 RegistrationSubtest * subtest; // Current subtest.
15389 char * outputFilePath; // Path of test result output file. If NULL, stdout will be used.
15390 OutputFormatType outputFormat; // Format of test results output.
15391 CFStringRef computerNamePrev; // Previous ComputerName.
15392 CFStringRef localHostNamePrev; // Previous LocalHostName.
15393 NanoTime64 startTime; // Test's start time.
15394 char * computerName; // Temporary ComputerName to set during testing. (malloc'd)
15395 char * localHostName; // Temporary LocalHostName to set during testing. (malloc'd)
15396 CFStringEncoding computerNamePrevEncoding; // Previous ComputerName's encoding.
15397 int subtestIndex; // Index of current subtest.
15398 Boolean computerNameSet; // True if a temporary ComputerName was set.
15399 Boolean localHostNameSet; // True if a temporary LocalHostName was set.
15400 Boolean failed; // True if at least one non-skipped subtest failed.
15401 Boolean forBATS; // True if the test is running in a BATS environment.
15402
15403 } RegistrationTest;
15404
15405 typedef enum
15406 {
15407 kRegistrationInterfaceSet_Null = 0,
15408 kRegistrationInterfaceSet_All = 1,
15409 kRegistrationInterfaceSet_AllPlusAWDL = 2,
15410 kRegistrationInterfaceSet_LoopbackOnly = 3,
15411 kRegistrationInterfaceSet_AWDLOnly = 4
15412
15413 } RegistrationInterfaceSet;
15414
15415 typedef struct
15416 {
15417 RegistrationInterfaceSet interfaceSet; // Interfaces to register the service over.
15418 Boolean useDefaultName; // True if registration is to use the default service name.
15419 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15420
15421 } RegistrationSubtestParams;
15422
15423 static const RegistrationSubtestParams kRegistrationSubtestParams[] =
15424 {
15425 { kRegistrationInterfaceSet_All, true, false },
15426 { kRegistrationInterfaceSet_All, false, false },
15427 { kRegistrationInterfaceSet_AllPlusAWDL, true, false },
15428 { kRegistrationInterfaceSet_AllPlusAWDL, false, false },
15429 { kRegistrationInterfaceSet_LoopbackOnly, true, false },
15430 { kRegistrationInterfaceSet_LoopbackOnly, false, false },
15431 { kRegistrationInterfaceSet_AWDLOnly, true, false },
15432 { kRegistrationInterfaceSet_AWDLOnly, false, false },
15433 { kRegistrationInterfaceSet_All, true, true },
15434 { kRegistrationInterfaceSet_All, false, true },
15435 { kRegistrationInterfaceSet_AllPlusAWDL, true, true },
15436 { kRegistrationInterfaceSet_AllPlusAWDL, false, true },
15437 { kRegistrationInterfaceSet_LoopbackOnly, true, true },
15438 { kRegistrationInterfaceSet_LoopbackOnly, false, true },
15439 { kRegistrationInterfaceSet_AWDLOnly, true, true },
15440 { kRegistrationInterfaceSet_AWDLOnly, false, true }
15441 };
15442
15443 typedef struct
15444 {
15445 NanoTime64 browseResultTime; // Per-interface browse result time.
15446 NanoTime64 querySRVResultTime; // Per-interface SRV record query result time.
15447 NanoTime64 queryTXTResultTime; // Per-interface TXT record query result time.
15448
15449 } RegistrationResultTimes;
15450
15451 typedef struct
15452 {
15453 MDNSInterfaceItem base; // Underlying MDNSInterface linked-list item.
15454 RegistrationResultTimes times; // Per-interface result times.
15455
15456 } RegistrationInterfaceItem;
15457
15458 struct RegistrationSubtest
15459 {
15460 DNSServiceRef registration; // DNS-SD service registration.
15461 DNSServiceRef connection; // Shared DNS-SD connection.
15462 DNSServiceRef browse; // DNS-SD browse for service's type.
15463 DNSServiceRef querySRV; // DNS-SD query request for service's SRV record.
15464 DNSServiceRef queryTXT; // DNS-SD query request for service's TXT record.
15465 CFMutableArrayRef unexpected; // Array of unexpected registration, browse, and query results.
15466 #if( TARGET_OS_WATCH )
15467 CFMutableArrayRef ignored; // Array of unexpected, but ignored, browse and query results.
15468 #endif
15469 const char * serviceName; // Service's name.
15470 char * serviceNameCustom; // Service's name if using a custom name. (malloc'd)
15471 char * serviceType; // Service's service type. (malloc'd)
15472 size_t serviceTypeLen; // C string length of service's service type.
15473 char * serviceFQDN; // Service's FQDN, i.e., name of its SRV and TXT records.
15474 uint8_t * txtPtr; // Pointer to service's TXT record data. (malloc'd)
15475 size_t txtLen; // Length of service's TXT record data.
15476 RegistrationInterfaceItem * ifList; // If ifIndex == 0, interfaces that service should register over.
15477 RegistrationResultTimes ifTimes; // If ifIndex != 0, result times for interface with that index.
15478 RegistrationTest * test; // Pointer to parent test.
15479 NanoTime64 startTime; // Subtest's start time.
15480 char * description; // Subtest's description. (malloc'd)
15481 uint32_t ifIndex; // Interface index used for service registration.
15482 uint16_t port; // Service's port number.
15483 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15484 Boolean includeAWDL; // True if the IncludeAWDL flag was used during registration.
15485 Boolean ifIsAWDL; // True if ifIndex is the index of an AWDL interface.
15486 Boolean skipped; // True if this subtest is to be skipped.
15487 Boolean registered; // True if the test service was successfully registered.
15488 Boolean useDefaultName; // True if the service is to use the default service name.
15489 };
15490
15491 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest );
15492 static void _RegistrationTestFree( RegistrationTest *inTest );
15493 static void _RegistrationTestBegin( void *inContext );
15494 static void _RegistrationTestProceed( RegistrationTest *inTest );
15495 static OSStatus _RegistrationTestStart( RegistrationTest *inTest );
15496 static void _RegistrationTestStop( RegistrationTest *inTest );
15497 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15498 static OSStatus
15499 _RegistrationTestStartSubtest(
15500 RegistrationTest * inTest,
15501 const RegistrationSubtestParams * inParams,
15502 Boolean * outSkipped );
15503 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest );
15504 static void _RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
15505 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
15506 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
15507 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest );
15508 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest );
15509 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15510 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
15511 static OSStatus
15512 _RegistrationTestCreateRandomTXTRecord(
15513 size_t inMinLen,
15514 size_t inMaxLen,
15515 uint8_t ** outTXTPtr,
15516 size_t * outTXTLen );
15517 static void DNSSD_API
15518 _RegistrationSubtestRegisterCallback(
15519 DNSServiceRef inSDRef,
15520 DNSServiceFlags inFlags,
15521 DNSServiceErrorType inError,
15522 const char * inName,
15523 const char * inType,
15524 const char * inDomain,
15525 void * inContext );
15526 static void DNSSD_API
15527 _RegistrationSubtestBrowseCallback(
15528 DNSServiceRef inSDRef,
15529 DNSServiceFlags inFlags,
15530 uint32_t inIfIndex,
15531 DNSServiceErrorType inError,
15532 const char * inServiceName,
15533 const char * inServiceType,
15534 const char * inDomain,
15535 void * inContext );
15536 static void DNSSD_API
15537 _RegistrationSubtestQueryCallback(
15538 DNSServiceRef inSDRef,
15539 DNSServiceFlags inFlags,
15540 uint32_t inIfIndex,
15541 DNSServiceErrorType inError,
15542 const char * inName,
15543 uint16_t inType,
15544 uint16_t inClass,
15545 uint16_t inRDataLen,
15546 const void * inRDataPtr,
15547 uint32_t inTTL,
15548 void * inContext );
15549 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
15550 static RegistrationResultTimes *
15551 _RegistrationSubtestGetInterfaceResultTimes(
15552 RegistrationSubtest * inSubtest,
15553 uint32_t inIfIndex,
15554 Boolean * outIsAWDL );
15555 static void _RegistrationTestTimerHandler( void *inContext );
15556 #if( TARGET_OS_WATCH )
15557 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName );
15558 #endif
15559
15560 static void RegistrationTestCmd( void )
15561 {
15562 OSStatus err;
15563 RegistrationTest * test;
15564
15565 err = _RegistrationTestCreate( &test );
15566 require_noerr( err, exit );
15567
15568 if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
15569 if( gRegistrationTest_OutputFilePath )
15570 {
15571 test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
15572 require_action( test->outputFilePath, exit, err = kNoMemoryErr );
15573 }
15574
15575 err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
15576 require_noerr_quiet( err, exit );
15577
15578 dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
15579 dispatch_main();
15580
15581 exit:
15582 if( test ) _RegistrationTestFree( test );
15583 ErrQuit( 1, "error: %#m\n", err );
15584 }
15585
15586 //===========================================================================================================================
15587
15588 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest )
15589 {
15590 OSStatus err;
15591 RegistrationTest * obj;
15592
15593 obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
15594 require_action( obj, exit, err = kNoMemoryErr );
15595
15596 obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15597 require_action( obj->subtestReports, exit, err = kNoMemoryErr );
15598
15599 *outTest = obj;
15600 obj = NULL;
15601 err = kNoErr;
15602
15603 exit:
15604 if( obj ) _RegistrationTestFree( obj );
15605 return( err );
15606 }
15607
15608 //===========================================================================================================================
15609
15610 static void _RegistrationTestFree( RegistrationTest *inTest )
15611 {
15612 check( !inTest->timer );
15613 check( !inTest->sigSourceINT );
15614 check( !inTest->sigSourceTERM );
15615 check( !inTest->computerNameSet );
15616 check( !inTest->localHostNameSet );
15617 check( !inTest->subtest );
15618 ForgetCF( &inTest->subtestReports );
15619 ForgetMem( &inTest->outputFilePath );
15620 ForgetCF( &inTest->computerNamePrev );
15621 ForgetCF( &inTest->localHostNamePrev );
15622 ForgetMem( &inTest->computerName );
15623 ForgetMem( &inTest->localHostName );
15624 }
15625
15626 //===========================================================================================================================
15627
15628 static void _RegistrationTestBegin( void *inContext )
15629 {
15630 _RegistrationTestProceed( (RegistrationTest *) inContext );
15631 }
15632
15633 //===========================================================================================================================
15634
15635 static void _RegistrationTestProceed( RegistrationTest *inTest )
15636 {
15637 OSStatus err;
15638 Boolean skippedSubtest;
15639
15640 do
15641 {
15642 int subtestIndex;
15643
15644 if( !inTest->startTime )
15645 {
15646 err = _RegistrationTestStart( inTest );
15647 require_noerr_quiet( err, exit );
15648
15649 inTest->startTime = NanoTimeGetCurrent();
15650 }
15651 else
15652 {
15653 err = _RegistrationTestEndSubtest( inTest );
15654 require_noerr( err, exit );
15655
15656 ++inTest->subtestIndex;
15657 }
15658
15659 subtestIndex = inTest->subtestIndex;
15660 if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
15661 {
15662 err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
15663 require_noerr_quiet( err, exit );
15664 }
15665 else
15666 {
15667 _RegistrationTestEnd( inTest );
15668 }
15669
15670 } while( skippedSubtest );
15671
15672 exit:
15673 if( err ) _RegistrationTestExit( inTest, err );
15674 }
15675
15676 //===========================================================================================================================
15677
15678 static void _RegistrationTestSignalHandler( void *inContext );
15679
15680 static OSStatus _RegistrationTestStart( RegistrationTest *inTest )
15681 {
15682 OSStatus err;
15683 char tag[ 6 + 1 ];
15684
15685 // Save original ComputerName and LocalHostName.
15686
15687 check( !inTest->computerNamePrev );
15688 inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
15689 err = map_scerror( inTest->computerNamePrev );
15690 require_noerr( err, exit );
15691
15692 check( !inTest->localHostNamePrev );
15693 inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
15694 err = map_scerror( inTest->localHostNamePrev );
15695 require_noerr( err, exit );
15696
15697 // Generate a unique test ComputerName.
15698
15699 check( !inTest->computerName );
15700 ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
15701 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15702 require_action( inTest->computerName, exit, err = kNoMemoryErr );
15703
15704 // Generate a unique test LocalHostName.
15705
15706 check( !inTest->localHostName );
15707 ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
15708 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15709 require_action( inTest->localHostName, exit, err = kNoMemoryErr );
15710
15711 // Set up SIGINT signal handler.
15712
15713 signal( SIGINT, SIG_IGN );
15714 check( !inTest->sigSourceINT );
15715 err = DispatchSignalSourceCreate( SIGINT, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceINT );
15716 require_noerr( err, exit );
15717 dispatch_resume( inTest->sigSourceINT );
15718
15719 // Set up SIGTERM signal handler.
15720
15721 signal( SIGTERM, SIG_IGN );
15722 check( !inTest->sigSourceTERM );
15723 err = DispatchSignalSourceCreate( SIGTERM, _RegistrationTestSignalHandler, inTest, &inTest->sigSourceTERM );
15724 require_noerr( err, exit );
15725 dispatch_resume( inTest->sigSourceTERM );
15726
15727 // Set test ComputerName.
15728
15729 check( !inTest->computerNameSet );
15730 err = _SetComputerNameWithUTF8CString( inTest->computerName );
15731 require_noerr( err, exit );
15732 inTest->computerNameSet = true;
15733
15734 // Set test LocalHostName.
15735
15736 check( !inTest->localHostNameSet );
15737 err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
15738 require_noerr( err, exit );
15739 inTest->localHostNameSet = true;
15740
15741 exit:
15742 if( err ) _RegistrationTestStop( inTest );
15743 return( err );
15744 }
15745
15746 static void _RegistrationTestSignalHandler( void *inContext )
15747 {
15748 RegistrationTest * const test = (RegistrationTest *) inContext;
15749
15750 FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15751
15752 _RegistrationTestExit( test, kCanceledErr );
15753 }
15754
15755 //===========================================================================================================================
15756
15757 static void _RegistrationTestStop( RegistrationTest *inTest )
15758 {
15759 OSStatus err;
15760
15761 dispatch_source_forget( &inTest->timer );
15762 dispatch_source_forget( &inTest->sigSourceINT );
15763 dispatch_source_forget( &inTest->sigSourceTERM );
15764 _RegistrationSubtestForget( &inTest->subtest );
15765 if( inTest->computerNameSet )
15766 {
15767 err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
15768 check_noerr( err );
15769 if( !err ) inTest->computerNameSet = false;
15770 }
15771 if( inTest->localHostNameSet )
15772 {
15773 err = _SetLocalHostName( inTest->localHostNamePrev );
15774 check_noerr( err );
15775 if( !err ) inTest->localHostNameSet = false;
15776 }
15777 }
15778
15779 //===========================================================================================================================
15780
15781 #define kRegistrationTestSubtestDurationSecs 5
15782
15783 static OSStatus
15784 _RegistrationTestStartSubtest(
15785 RegistrationTest * inTest,
15786 const RegistrationSubtestParams * inParams,
15787 Boolean * outSkipped )
15788 {
15789 OSStatus err;
15790 RegistrationSubtest * subtest;
15791 const char * interfaceDesc;
15792 DNSServiceFlags flags;
15793 char tag[ 6 + 1 ];
15794
15795 subtest = NULL;
15796 err = _RegistrationSubtestCreate( &subtest );
15797 require_noerr( err, exit );
15798
15799 subtest->test = inTest;
15800 subtest->useDefaultName = inParams->useDefaultName;
15801 subtest->useLODiscovery = inParams->useLODiscovery;
15802
15803 // Determine registration interfaces.
15804
15805 switch( inParams->interfaceSet )
15806 {
15807 case kRegistrationInterfaceSet_All:
15808 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15809
15810 if( !subtest->useLODiscovery )
15811 {
15812 err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
15813 require_noerr( err, exit );
15814 }
15815 interfaceDesc = "all interfaces (excluding AWDL)";
15816 break;
15817
15818 case kRegistrationInterfaceSet_AllPlusAWDL:
15819 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
15820 subtest->includeAWDL = true;
15821
15822 if( !subtest->useLODiscovery )
15823 {
15824 err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
15825 require_noerr( err, exit );
15826 }
15827 interfaceDesc = "all interfaces (including AWDL)";
15828 break;
15829
15830 case kRegistrationInterfaceSet_LoopbackOnly:
15831 subtest->ifIndex = if_nametoindex( "lo0" );
15832 if( subtest->ifIndex == 0 )
15833 {
15834 FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
15835 err = kNoResourcesErr;
15836 goto exit;
15837 }
15838 interfaceDesc = "loopback interface";
15839 break;
15840
15841 case kRegistrationInterfaceSet_AWDLOnly:
15842 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
15843 if( err == kNotFoundErr )
15844 {
15845 FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
15846 subtest->skipped = true;
15847 err = kNoErr;
15848 }
15849 require_noerr( err, exit );
15850
15851 subtest->ifIsAWDL = true;
15852 interfaceDesc = "AWDL interface";
15853 break;
15854
15855 default:
15856 err = kParamErr;
15857 goto exit;
15858 }
15859
15860 // Create description.
15861
15862 ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
15863 interfaceDesc, subtest->useDefaultName ? "default" : "custom",
15864 subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
15865 require_action( subtest->description, exit, err = kNoMemoryErr );
15866
15867 if( subtest->skipped )
15868 {
15869 subtest->startTime = NanoTimeGetCurrent();
15870 }
15871 else
15872 {
15873 // Generate a service name.
15874
15875 if( subtest->useDefaultName )
15876 {
15877 subtest->serviceName = inTest->computerName;
15878 }
15879 else
15880 {
15881 ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
15882 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15883 require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
15884
15885 subtest->serviceName = subtest->serviceNameCustom;
15886 }
15887
15888 // Generate a service type.
15889
15890 ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
15891 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
15892 require_action( subtest->serviceType, exit, err = kNoMemoryErr );
15893
15894 subtest->serviceTypeLen = strlen( subtest->serviceType );
15895
15896 // Create SRV and TXT record name FQDN.
15897
15898 ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
15899 require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
15900
15901 // Generate a port number.
15902
15903 subtest->port = (uint16_t) RandomRange( 60000, 65535 );
15904
15905 // Generate TXT record data.
15906
15907 err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
15908 require_noerr( err, exit );
15909
15910 // Register service.
15911
15912 subtest->startTime = NanoTimeGetCurrent();
15913
15914 flags = kDNSServiceFlagsNoAutoRename;
15915 if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
15916 err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
15917 subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
15918 NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
15919 _RegistrationSubtestRegisterCallback, subtest );
15920 require_noerr( err, exit );
15921
15922 err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
15923 require_noerr( err, exit );
15924
15925 // Start timer.
15926
15927 check( !inTest->timer );
15928 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
15929 INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15930 _RegistrationTestTimerHandler, inTest, &inTest->timer );
15931 require_noerr( err, exit );
15932 dispatch_resume( inTest->timer );
15933 }
15934
15935 *outSkipped = subtest->skipped;
15936
15937 check( !inTest->subtest );
15938 inTest->subtest = subtest;
15939 subtest = NULL;
15940
15941 exit:
15942 _RegistrationSubtestForget( &subtest );
15943 return( err );
15944 }
15945
15946 //===========================================================================================================================
15947
15948 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15949 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15950 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15951 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15952 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15953 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15954 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15955 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15956 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15957 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15958 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15959 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15960 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15961 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15962 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15963 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15964 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15965 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15966 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15967 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15968 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15969 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15970 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15971 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15972 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15973 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15974 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15975 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15976 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15977 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15978
15979 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15980 #define kRegistrationTestResultType_Query CFSTR( "query" )
15981 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15982 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15983 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15984
15985 static OSStatus
15986 _RegistrationTestAppendMissingResults(
15987 CFMutableArrayRef inMissingResults,
15988 const RegistrationResultTimes * inTimes,
15989 uint32_t inIfIndex,
15990 const char * inIfName );
15991
15992 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest )
15993 {
15994 OSStatus err;
15995 RegistrationSubtest * subtest;
15996 CFMutableDictionaryRef subtestReport;
15997 CFMutableArrayRef missing;
15998 char * txtStr;
15999 NanoTime64 now;
16000 Boolean subtestFailed;
16001 char startTime[ 32 ];
16002 char endTime[ 32 ];
16003 char ifNameBuf[ IF_NAMESIZE + 1 ];
16004
16005 now = NanoTimeGetCurrent();
16006
16007 subtest = inTest->subtest;
16008 inTest->subtest = NULL;
16009 _RegistrationSubtestStop( subtest );
16010
16011 missing = NULL;
16012 subtestReport = NULL;
16013 txtStr = NULL;
16014 if( subtest->txtPtr )
16015 {
16016 err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, NULL, 0, &txtStr );
16017 require_noerr( err, exit );
16018 }
16019 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16020 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16021 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
16022 "{"
16023 "%kO=%s" // description
16024 "%kO=%s" // startTime
16025 "%kO=%s" // endTime
16026 "%kO=%s" // serviceFQDN
16027 "%kO=%lli" // ifIndex
16028 "%kO=%s" // ifName
16029 "%kO=%lli" // port
16030 "%kO=%s" // txt
16031 "%kO=%b" // registered
16032 "%kO=%b" // usedDefaultName
16033 "%kO=%b" // usedLODiscovery
16034 "}",
16035 kRegistrationTestReportKey_Description, subtest->description,
16036 kRegistrationTestReportKey_StartTime, startTime,
16037 kRegistrationTestReportKey_EndTime, endTime,
16038 kRegistrationTestReportKey_ServiceFQDN, subtest->serviceFQDN,
16039 kRegistrationTestReportKey_InterfaceIndex, (int64_t) subtest->ifIndex,
16040 kRegistrationTestReportKey_InterfaceName, if_indextoname( subtest->ifIndex, ifNameBuf ),
16041 kRegistrationTestReportKey_Port, (int64_t) subtest->port,
16042 kRegistrationTestReportKey_TXT, txtStr,
16043 kRegistrationTestReportKey_Registered, (int) subtest->registered,
16044 kRegistrationTestReportKey_UsedDefaultName, (int) subtest->useDefaultName,
16045 kRegistrationTestReportKey_UsedLODiscovery, (int) subtest->useLODiscovery );
16046 ForgetMem( &txtStr );
16047 require_noerr( err, exit );
16048
16049 if( !subtest->skipped && subtest->registered )
16050 {
16051 missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16052 require_action( missing, exit, err = kNoMemoryErr );
16053
16054 if( subtest->ifList )
16055 {
16056 RegistrationInterfaceItem * item;
16057
16058 for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16059 {
16060 #if( TARGET_OS_WATCH )
16061 if( inTest->forBATS && item->base.isWiFi ) continue;
16062 #endif
16063 err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
16064 require_noerr( err, exit );
16065 }
16066 }
16067 else
16068 {
16069 err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
16070 require_noerr( err, exit );
16071 }
16072
16073 subtestFailed = false;
16074 if( CFArrayGetCount( missing ) > 0 )
16075 {
16076 subtestFailed = true;
16077 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
16078 }
16079 if( CFArrayGetCount( subtest->unexpected ) > 0 )
16080 {
16081 subtestFailed = true;
16082 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
16083 }
16084 #if( TARGET_OS_WATCH )
16085 if( CFArrayGetCount( subtest->ignored ) > 0 )
16086 {
16087 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
16088 }
16089 #endif
16090 }
16091 else
16092 {
16093 subtestFailed = true;
16094 }
16095
16096 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
16097 if( subtestFailed )
16098 {
16099 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
16100 if( !subtest->skipped ) inTest->failed = true;
16101 }
16102 CFArrayAppendValue( inTest->subtestReports, subtestReport );
16103
16104 exit:
16105 CFReleaseNullSafe( missing );
16106 CFReleaseNullSafe( subtestReport );
16107 _RegistrationSubtestFree( subtest );
16108 return( err );
16109 }
16110
16111 static OSStatus
16112 _RegistrationTestAppendMissingResult(
16113 CFMutableArrayRef inMissingResults,
16114 CFStringRef inType,
16115 uint32_t inIfIndex,
16116 const char * inIfName );
16117
16118 static OSStatus
16119 _RegistrationTestAppendMissingResults(
16120 CFMutableArrayRef inMissingResults,
16121 const RegistrationResultTimes * inTimes,
16122 uint32_t inIfIndex,
16123 const char * inIfName )
16124 {
16125 OSStatus err;
16126
16127 if( !inTimes->browseResultTime )
16128 {
16129 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
16130 inIfIndex, inIfName );
16131 require_noerr( err, exit );
16132 }
16133 if( !inTimes->querySRVResultTime )
16134 {
16135 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
16136 inIfIndex, inIfName );
16137 require_noerr( err, exit );
16138 }
16139 if( !inTimes->queryTXTResultTime )
16140 {
16141 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
16142 inIfIndex, inIfName );
16143 require_noerr( err, exit );
16144 }
16145 err = kNoErr;
16146
16147 exit:
16148 return( err );
16149 }
16150
16151 static OSStatus
16152 _RegistrationTestAppendMissingResult(
16153 CFMutableArrayRef inMissingResults,
16154 CFStringRef inType,
16155 uint32_t inIfIndex,
16156 const char * inIfName )
16157 {
16158 OSStatus err;
16159 char ifName[ IF_NAMESIZE + 1 ];
16160
16161 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
16162 "{"
16163 "%kO=%O" // resultType
16164 "%kO=%lli" // ifIndex
16165 "%kO=%s" // ifName
16166 "}",
16167 kRegistrationTestReportKey_ResultType, inType,
16168 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16169 kRegistrationTestReportKey_InterfaceName, inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
16170 return( err );
16171 }
16172
16173 //===========================================================================================================================
16174
16175 static void _RegistrationTestEnd( RegistrationTest *inTest )
16176 {
16177 OSStatus err;
16178 NanoTime64 now;
16179 CFPropertyListRef plist;
16180 char startTime[ 32 ];
16181 char endTime[ 32 ];
16182
16183 now = NanoTimeGetCurrent();
16184 _NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
16185 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16186
16187 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16188 "{"
16189 "%kO=%s" // startTime
16190 "%kO=%s" // endTime
16191 "%kO=%s" // computerName
16192 "%kO=%s" // localHostName
16193 "%kO=%O" // subtests
16194 "%kO=%b" // pass
16195 "}",
16196 kRegistrationTestReportKey_StartTime, startTime,
16197 kRegistrationTestReportKey_EndTime, endTime,
16198 kRegistrationTestReportKey_ComputerName, inTest->computerName,
16199 kRegistrationTestReportKey_LocalHostName, inTest->localHostName,
16200 kRegistrationTestReportKey_Subtests, inTest->subtestReports,
16201 kRegistrationTestReportKey_Pass, inTest->failed ? false : true );
16202 require_noerr( err, exit );
16203
16204 err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
16205 CFRelease( plist );
16206 require_noerr( err, exit );
16207
16208 exit:
16209 _RegistrationTestExit( inTest, err );
16210 }
16211
16212 //===========================================================================================================================
16213
16214 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
16215 {
16216 int exitCode;
16217
16218 if( inError )
16219 {
16220 FPrintF( stderr, "error: %#m\n", inError );
16221 exitCode = 1;
16222 }
16223 else
16224 {
16225 exitCode = inTest->failed ? 2 : 0;
16226 }
16227 _RegistrationTestForget( &inTest );
16228 exit( exitCode );
16229 }
16230
16231 //===========================================================================================================================
16232
16233 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
16234 {
16235 OSStatus err;
16236 RegistrationSubtest * obj;
16237
16238 obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
16239 require_action( obj, exit, err = kNoMemoryErr );
16240
16241 obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16242 require_action( obj->unexpected, exit, err = kNoMemoryErr );
16243
16244 #if( TARGET_OS_WATCH )
16245 obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
16246 require_action( obj->ignored, exit, err = kNoMemoryErr );
16247 #endif
16248
16249 *outSubtest = obj;
16250 obj = NULL;
16251 err = kNoErr;
16252
16253 exit:
16254 if( obj ) _RegistrationSubtestFree( obj );
16255 return( err );
16256 }
16257
16258 //===========================================================================================================================
16259
16260 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest )
16261 {
16262 check( !inSubtest->registration );
16263 check( !inSubtest->browse );
16264 check( !inSubtest->querySRV );
16265 check( !inSubtest->queryTXT );
16266 check( !inSubtest->connection );
16267 ForgetMem( &inSubtest->serviceNameCustom );
16268 ForgetMem( &inSubtest->serviceType );
16269 ForgetMem( &inSubtest->serviceFQDN );
16270 ForgetMem( &inSubtest->txtPtr );
16271 ForgetCF( &inSubtest->unexpected );
16272 #if( TARGET_OS_WATCH )
16273 ForgetCF( &inSubtest->ignored );
16274 #endif
16275 _MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
16276 ForgetMem( &inSubtest->description );
16277 free( inSubtest );
16278 }
16279
16280 //===========================================================================================================================
16281
16282 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest )
16283 {
16284 DNSServiceForget( &inSubtest->registration );
16285 DNSServiceForget( &inSubtest->browse );
16286 DNSServiceForget( &inSubtest->querySRV );
16287 DNSServiceForget( &inSubtest->queryTXT );
16288 DNSServiceForget( &inSubtest->connection );
16289 }
16290
16291 //===========================================================================================================================
16292
16293 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
16294 {
16295 OSStatus err;
16296 RegistrationInterfaceItem * list;
16297 const MDNSInterfaceSubset subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
16298
16299 err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
16300 require_noerr( err, exit );
16301
16302 *outList = list;
16303
16304 exit:
16305 return( err );
16306 }
16307
16308 //===========================================================================================================================
16309
16310 static OSStatus
16311 _RegistrationTestCreateRandomTXTRecord(
16312 size_t inMinLen,
16313 size_t inMaxLen,
16314 uint8_t ** outTXTPtr,
16315 size_t * outTXTLen )
16316 {
16317 OSStatus err;
16318 uint8_t * ptr;
16319 const uint8_t * txtEnd;
16320 uint8_t * txtPtr = NULL;
16321 size_t txtLen;
16322
16323 require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
16324
16325 txtLen = RandomRange( inMinLen, inMaxLen );
16326 txtPtr = (uint8_t *) malloc( txtLen + 1 );
16327 require_action( txtPtr, exit, err = kNoMemoryErr );
16328
16329 _RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
16330
16331 ptr = txtPtr;
16332 txtEnd = &txtPtr[ txtLen ];
16333 while( ptr < txtEnd )
16334 {
16335 size_t maxLen, len;
16336
16337 maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
16338 len = RandomRange( 1, 255 );
16339 if( len > maxLen ) len = maxLen;
16340
16341 *ptr = (uint8_t) len;
16342 ptr += ( 1 + len );
16343 }
16344 check( ptr == txtEnd );
16345
16346 if( outTXTPtr )
16347 {
16348 *outTXTPtr = txtPtr;
16349 txtPtr = NULL;
16350 }
16351 if( outTXTLen ) *outTXTLen = txtLen;
16352 err = kNoErr;
16353
16354 exit:
16355 FreeNullSafe( txtPtr );
16356 return( err );
16357 }
16358
16359 //===========================================================================================================================
16360
16361 static void DNSSD_API
16362 _RegistrationSubtestRegisterCallback(
16363 DNSServiceRef inSDRef,
16364 DNSServiceFlags inFlags,
16365 DNSServiceErrorType inError,
16366 const char * inServiceName,
16367 const char * inServiceType,
16368 const char * inDomain,
16369 void * inContext )
16370 {
16371 OSStatus err;
16372 const NanoTime64 now = NanoTimeGetCurrent();
16373 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16374
16375 Unused( inSDRef );
16376
16377 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
16378 ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16379 _RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
16380 ( strcasecmp( inDomain, "local." ) == 0 ) )
16381 {
16382 if( !subtest->registered )
16383 {
16384 DNSServiceRef sdRef;
16385 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16386
16387 subtest->registered = true;
16388
16389 // Create shared connection.
16390
16391 check( !subtest->connection );
16392 err = DNSServiceCreateConnection( &subtest->connection );
16393 require_noerr( err, exit );
16394
16395 err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
16396 require_noerr( err, exit );
16397
16398 // Start browse.
16399
16400 check( !subtest->browse );
16401 sdRef = subtest->connection;
16402 err = DNSServiceBrowse( &sdRef, flags,
16403 subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
16404 subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
16405 require_noerr( err, exit );
16406
16407 subtest->browse = sdRef;
16408 }
16409 }
16410 else
16411 {
16412 char timestamp[ 32 ];
16413
16414 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
16415 "{"
16416 "%kO=%O" // resultType
16417 "%kO=%s" // timestamp
16418 "%kO=%lli" // flags
16419 "%kO=%lli" // error
16420 "%kO=%s" // serviceName
16421 "%kO=%s" // serviceType
16422 "%kO=%s" // domain
16423 "}",
16424 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Registration,
16425 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16426 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16427 kRegistrationTestReportKey_Error, (int64_t) inError,
16428 kRegistrationTestReportKey_ServiceName, inServiceName,
16429 kRegistrationTestReportKey_ServiceType, inServiceType,
16430 kRegistrationTestReportKey_Domain, inDomain );
16431 require_noerr( err, exit );
16432 }
16433 err = kNoErr;
16434
16435 exit:
16436 if( err ) _RegistrationTestExit( subtest->test, err );
16437 }
16438
16439 //===========================================================================================================================
16440
16441 static void DNSSD_API
16442 _RegistrationSubtestBrowseCallback(
16443 DNSServiceRef inSDRef,
16444 DNSServiceFlags inFlags,
16445 uint32_t inIfIndex,
16446 DNSServiceErrorType inError,
16447 const char * inServiceName,
16448 const char * inServiceType,
16449 const char * inDomain,
16450 void * inContext )
16451 {
16452 OSStatus err;
16453 NanoTime64 now;
16454 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16455 Boolean serviceIsCorrect, resultIsExpected;
16456
16457 Unused( inSDRef );
16458
16459 now = NanoTimeGetCurrent();
16460 if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
16461 _RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
16462 {
16463 serviceIsCorrect = true;
16464 }
16465 else
16466 {
16467 serviceIsCorrect = false;
16468 }
16469
16470 resultIsExpected = false;
16471 if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
16472 {
16473 RegistrationResultTimes * times;
16474
16475 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
16476 if( times )
16477 {
16478 DNSServiceRef sdRef;
16479 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
16480 uint32_t ifIndex;
16481
16482 resultIsExpected = true;
16483 if( !times->browseResultTime ) times->browseResultTime = now;
16484
16485 ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
16486 if( !subtest->querySRV )
16487 {
16488 // Start SRV record query.
16489
16490 sdRef = subtest->connection;
16491 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
16492 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16493 require_noerr( err, exit );
16494
16495 subtest->querySRV = sdRef;
16496 }
16497 if( !subtest->queryTXT )
16498 {
16499 // Start TXT record query.
16500
16501 sdRef = subtest->connection;
16502 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
16503 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
16504 require_noerr( err, exit );
16505
16506 subtest->queryTXT = sdRef;
16507 }
16508 }
16509 }
16510
16511 if( !resultIsExpected )
16512 {
16513 CFMutableArrayRef resultArray;
16514 char timestamp[ 32 ];
16515 const char * ifNamePtr;
16516 char ifNameBuf[ IF_NAMESIZE + 1 ];
16517
16518 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16519 resultArray = subtest->unexpected;
16520 #if( TARGET_OS_WATCH )
16521 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16522 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
16523 {
16524 resultArray = subtest->ignored;
16525 }
16526 #endif
16527 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
16528 "{"
16529 "%kO=%O" // resultType
16530 "%kO=%s" // timestamp
16531 "%kO=%lli" // flags
16532 "%kO=%lli" // ifIndex
16533 "%kO=%s" // ifName
16534 "%kO=%lli" // error
16535 "%kO=%s" // serviceName
16536 "%kO=%s" // serviceType
16537 "%kO=%s" // domain
16538 "}",
16539 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Browse,
16540 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16541 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16542 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16543 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16544 kRegistrationTestReportKey_Error, (int64_t) inError,
16545 kRegistrationTestReportKey_ServiceName, inServiceName,
16546 kRegistrationTestReportKey_ServiceType, inServiceType,
16547 kRegistrationTestReportKey_Domain, inDomain );
16548 require_noerr( err, exit );
16549 }
16550 err = kNoErr;
16551
16552 exit:
16553 if( err ) _RegistrationTestExit( subtest->test, err );
16554 }
16555
16556 //===========================================================================================================================
16557
16558 static Boolean
16559 _RegistrationSubtestIsSRVRecordDataValid(
16560 RegistrationSubtest * inSubtest,
16561 const uint8_t * inRDataPtr,
16562 size_t inRDataLen,
16563 Boolean inExpectRandHostname );
16564
16565 static void DNSSD_API
16566 _RegistrationSubtestQueryCallback(
16567 DNSServiceRef inSDRef,
16568 DNSServiceFlags inFlags,
16569 uint32_t inIfIndex,
16570 DNSServiceErrorType inError,
16571 const char * inName,
16572 uint16_t inType,
16573 uint16_t inClass,
16574 uint16_t inRDataLen,
16575 const void * inRDataPtr,
16576 uint32_t inTTL,
16577 void * inContext )
16578 {
16579 OSStatus err;
16580 NanoTime64 now;
16581 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
16582 Boolean resultIsExpected;
16583
16584 Unused( inSDRef );
16585 Unused( inTTL );
16586
16587 now = NanoTimeGetCurrent();
16588 resultIsExpected = false;
16589 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
16590 ( inClass == kDNSServiceClass_IN ) )
16591 {
16592 RegistrationResultTimes * times;
16593 Boolean isAWDL;
16594
16595 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
16596 if( times )
16597 {
16598 if( inType == kDNSServiceType_SRV )
16599 {
16600 Boolean expectRandHostname;
16601
16602 if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
16603 {
16604 expectRandHostname = true;
16605 }
16606 else
16607 {
16608 expectRandHostname = false;
16609 }
16610 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16611 {
16612 resultIsExpected = true;
16613 if( !times->querySRVResultTime ) times->querySRVResultTime = now;
16614 }
16615 }
16616 else if( inType == kDNSServiceType_TXT )
16617 {
16618 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16619 {
16620 resultIsExpected = true;
16621 if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
16622 }
16623 }
16624 }
16625 }
16626
16627 if( !resultIsExpected )
16628 {
16629 CFMutableArrayRef resultArray;
16630 CFMutableDictionaryRef resultDict;
16631 CFStringRef rdataKey;
16632 char * rdataStr;
16633 const char * ifNamePtr;
16634 char timestamp[ 32 ];
16635 char ifNameBuf[ IF_NAMESIZE + 1 ];
16636
16637 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
16638 resultArray = subtest->unexpected;
16639 #if( TARGET_OS_WATCH )
16640 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
16641 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
16642 ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
16643 {
16644 if( inType == kDNSServiceType_SRV )
16645 {
16646 const Boolean expectRandHostname = subtest->includeAWDL ? true : false;
16647
16648 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
16649 {
16650 resultArray = subtest->ignored;
16651 }
16652 }
16653 else if( inType == kDNSServiceType_TXT )
16654 {
16655 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
16656 {
16657 resultArray = subtest->ignored;
16658 }
16659 }
16660 }
16661 #endif
16662 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
16663 "{"
16664 "%kO=%O" // resultType
16665 "%kO=%s" // timestamp
16666 "%kO=%lli" // flags
16667 "%kO=%lli" // ifIndex
16668 "%kO=%s" // ifName
16669 "%kO=%lli" // error
16670 "%kO=%s" // serviceFQDN
16671 "%kO=%lli" // recordType
16672 "%kO=%lli" // class
16673 "}",
16674 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Query,
16675 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
16676 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
16677 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
16678 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
16679 kRegistrationTestReportKey_Error, (int64_t) inError,
16680 kRegistrationTestReportKey_ServiceFQDN, inName,
16681 kRegistrationTestReportKey_RecordType, (int64_t) inType,
16682 kRegistrationTestReportKey_RecordClass, (int64_t) inClass );
16683 require_noerr( err, exit );
16684
16685 rdataStr = NULL;
16686 DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
16687 if( rdataStr )
16688 {
16689 rdataKey = kRegistrationTestReportKey_RDataFormatted;
16690 }
16691 else
16692 {
16693 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16694 require_action( rdataStr, exit, err = kNoMemoryErr );
16695
16696 rdataKey = kRegistrationTestReportKey_RDataHexString;
16697 }
16698 err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
16699 ForgetMem( &rdataStr );
16700 if( err ) CFRelease( resultDict );
16701 require_noerr( err, exit );
16702
16703 CFArrayAppendValue( resultArray, resultDict );
16704 CFRelease( resultDict );
16705 }
16706 err = kNoErr;
16707
16708 exit:
16709 if( err ) _RegistrationTestExit( subtest->test, err );
16710 }
16711
16712 static Boolean
16713 _RegistrationSubtestIsSRVRecordDataValid(
16714 RegistrationSubtest * inSubtest,
16715 const uint8_t * inRDataPtr,
16716 size_t inRDataLen,
16717 Boolean inExpectRandHostname )
16718 {
16719 const dns_fixed_fields_srv * fields;
16720 const uint8_t * const end = &inRDataPtr[ inRDataLen ];
16721 const uint8_t * label;
16722 size_t len;
16723 uint16_t port;
16724 Boolean isValid;
16725
16726 isValid = false;
16727 require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
16728
16729 fields = (const dns_fixed_fields_srv *) inRDataPtr;
16730 port = dns_fixed_fields_srv_get_port( fields );
16731 require_quiet( port == inSubtest->port, exit );
16732
16733 // First target label should be a UUID string for the AWDL interface.
16734
16735 label = (const uint8_t *) &fields[ 1 ];
16736 require_quiet( ( end - label ) >= 1, exit );
16737
16738 len = label[ 0 ];
16739 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16740
16741 if( inExpectRandHostname )
16742 {
16743 if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
16744 }
16745 else
16746 {
16747 if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
16748 }
16749
16750 // Second target label should be "local".
16751
16752 label = &label[ 1 + len ];
16753 require_quiet( ( end - label ) >= 1, exit );
16754
16755 len = label[ 0 ];
16756 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
16757
16758 if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
16759
16760 // Third target label should be the root label.
16761
16762 label = &label[ 1 + len ];
16763 require_quiet( ( end - label ) >= 1, exit );
16764
16765 len = label[ 0 ];
16766 if( len != 0 ) goto exit;
16767
16768 isValid = true;
16769
16770 exit:
16771 return( isValid );
16772 }
16773
16774 //===========================================================================================================================
16775
16776 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
16777 {
16778 if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
16779 {
16780 const char * const ptr = &inServiceType[ inSubtest->serviceTypeLen ];
16781
16782 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
16783 }
16784 return( false );
16785 }
16786
16787 //===========================================================================================================================
16788
16789 static RegistrationResultTimes *
16790 _RegistrationSubtestGetInterfaceResultTimes(
16791 RegistrationSubtest * inSubtest,
16792 uint32_t inIfIndex,
16793 Boolean * outIsAWDL )
16794 {
16795 if( inSubtest->ifList )
16796 {
16797 RegistrationInterfaceItem * item;
16798
16799 for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
16800 {
16801 if( inIfIndex == item->base.ifIndex )
16802 {
16803 if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
16804 return( &item->times );
16805 }
16806 }
16807 }
16808 else
16809 {
16810 if( inIfIndex == inSubtest->ifIndex )
16811 {
16812 if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
16813 return( &inSubtest->ifTimes );
16814 }
16815 }
16816 return( NULL );
16817 }
16818
16819 //===========================================================================================================================
16820
16821 static void _RegistrationTestTimerHandler( void *inContext )
16822 {
16823 RegistrationTest * const test = (RegistrationTest *) inContext;
16824
16825 dispatch_source_forget( &test->timer );
16826 _RegistrationTestProceed( test );
16827 }
16828
16829 //===========================================================================================================================
16830
16831 #if( TARGET_OS_WATCH )
16832 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName )
16833 {
16834 NetTransportType type = kNetTransportType_Undefined;
16835
16836 SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
16837 return( ( type == kNetTransportType_WiFi ) ? true : false );
16838 }
16839 #endif
16840
16841 //===========================================================================================================================
16842 // SSDPDiscoverCmd
16843 //===========================================================================================================================
16844
16845 #define kSSDPPort 1900
16846
16847 typedef struct
16848 {
16849 HTTPHeader header; // HTTP header object for sending and receiving.
16850 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
16851 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
16852 int receiveSecs; // After send, the amount of time to spend receiving.
16853 uint32_t ifindex; // Index of the interface over which to send the query.
16854 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
16855 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
16856
16857 } SSDPDiscoverContext;
16858
16859 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
16860 static void SSDPDiscoverReadHandler( void *inContext );
16861 static int SocketToPortNumber( SocketRef inSock );
16862 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
16863
16864 static void SSDPDiscoverCmd( void )
16865 {
16866 OSStatus err;
16867 struct timeval now;
16868 SSDPDiscoverContext * context;
16869 dispatch_source_t signalSource = NULL;
16870 SocketRef sockV4 = kInvalidSocketRef;
16871 SocketRef sockV6 = kInvalidSocketRef;
16872 ssize_t n;
16873 int sendCount;
16874
16875 // Set up SIGINT handler.
16876
16877 signal( SIGINT, SIG_IGN );
16878 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
16879 require_noerr( err, exit );
16880 dispatch_resume( signalSource );
16881
16882 // Check command parameters.
16883
16884 if( gSSDPDiscover_ReceiveSecs < -1 )
16885 {
16886 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
16887 err = kParamErr;
16888 goto exit;
16889 }
16890
16891 // Create context.
16892
16893 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
16894 require_action( context, exit, err = kNoMemoryErr );
16895
16896 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
16897 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
16898 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
16899
16900 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
16901 require_noerr_quiet( err, exit );
16902
16903 // Set up IPv4 socket.
16904
16905 if( context->useIPv4 )
16906 {
16907 int port;
16908 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
16909 require_noerr( err, exit );
16910
16911 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
16912 require_noerr( err, exit );
16913
16914 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
16915 err = map_socket_noerr_errno( sockV4, err );
16916 require_noerr( err, exit );
16917 }
16918
16919 // Set up IPv6 socket.
16920
16921 if( context->useIPv6 )
16922 {
16923 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
16924 require_noerr( err, exit );
16925
16926 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
16927 require_noerr( err, exit );
16928
16929 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
16930 err = map_socket_noerr_errno( sockV6, err );
16931 require_noerr( err, exit );
16932 }
16933
16934 // Print prologue.
16935
16936 SSDPDiscoverPrintPrologue( context );
16937
16938 // Send mDNS query message.
16939
16940 sendCount = 0;
16941 if( IsValidSocket( sockV4 ) )
16942 {
16943 struct sockaddr_in mcastAddr4;
16944
16945 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
16946 SIN_LEN_SET( &mcastAddr4 );
16947 mcastAddr4.sin_family = AF_INET;
16948 mcastAddr4.sin_port = htons( kSSDPPort );
16949 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
16950
16951 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
16952 require_noerr( err, exit );
16953
16954 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
16955 (socklen_t) sizeof( mcastAddr4 ) );
16956 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
16957 if( err )
16958 {
16959 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
16960 ForgetSocket( &sockV4 );
16961 }
16962 else
16963 {
16964 if( gSSDPDiscover_Verbose )
16965 {
16966 gettimeofday( &now, NULL );
16967 FPrintF( stdout, "---\n" );
16968 FPrintF( stdout, "Send time: %{du:time}\n", &now );
16969 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
16970 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
16971 FPrintF( stdout, "Message size: %zu\n", context->header.len );
16972 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
16973 }
16974 ++sendCount;
16975 }
16976 }
16977
16978 if( IsValidSocket( sockV6 ) )
16979 {
16980 struct sockaddr_in6 mcastAddr6;
16981
16982 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
16983 SIN6_LEN_SET( &mcastAddr6 );
16984 mcastAddr6.sin6_family = AF_INET6;
16985 mcastAddr6.sin6_port = htons( kSSDPPort );
16986 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16987 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
16988 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
16989
16990 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
16991 require_noerr( err, exit );
16992
16993 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
16994 (socklen_t) sizeof( mcastAddr6 ) );
16995 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
16996 if( err )
16997 {
16998 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
16999 ForgetSocket( &sockV6 );
17000 }
17001 else
17002 {
17003 if( gSSDPDiscover_Verbose )
17004 {
17005 gettimeofday( &now, NULL );
17006 FPrintF( stdout, "---\n" );
17007 FPrintF( stdout, "Send time: %{du:time}\n", &now );
17008 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
17009 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
17010 FPrintF( stdout, "Message size: %zu\n", context->header.len );
17011 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
17012 }
17013 ++sendCount;
17014 }
17015 }
17016 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
17017
17018 // If there's no wait period after the send, then exit.
17019
17020 if( context->receiveSecs == 0 ) goto exit;
17021
17022 // Create dispatch read sources for socket(s).
17023
17024 if( IsValidSocket( sockV4 ) )
17025 {
17026 SocketContext * sockCtx;
17027
17028 err = SocketContextCreate( sockV4, context, &sockCtx );
17029 require_noerr( err, exit );
17030 sockV4 = kInvalidSocketRef;
17031
17032 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17033 &context->readSourceV4 );
17034 if( err ) ForgetSocketContext( &sockCtx );
17035 require_noerr( err, exit );
17036
17037 dispatch_resume( context->readSourceV4 );
17038 }
17039
17040 if( IsValidSocket( sockV6 ) )
17041 {
17042 SocketContext * sockCtx;
17043
17044 err = SocketContextCreate( sockV6, context, &sockCtx );
17045 require_noerr( err, exit );
17046 sockV6 = kInvalidSocketRef;
17047
17048 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
17049 &context->readSourceV6 );
17050 if( err ) ForgetSocketContext( &sockCtx );
17051 require_noerr( err, exit );
17052
17053 dispatch_resume( context->readSourceV6 );
17054 }
17055
17056 if( context->receiveSecs > 0 )
17057 {
17058 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
17059 Exit );
17060 }
17061 dispatch_main();
17062
17063 exit:
17064 ForgetSocket( &sockV4 );
17065 ForgetSocket( &sockV6 );
17066 dispatch_source_forget( &signalSource );
17067 exit( err ? 1 : 0 );
17068 }
17069
17070 static int SocketToPortNumber( SocketRef inSock )
17071 {
17072 OSStatus err;
17073 sockaddr_ip sip;
17074 socklen_t len;
17075
17076 len = (socklen_t) sizeof( sip );
17077 err = getsockname( inSock, &sip.sa, &len );
17078 err = map_socket_noerr_errno( inSock, err );
17079 check_noerr( err );
17080 return( err ? -1 : SockAddrGetPort( &sip ) );
17081 }
17082
17083 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
17084 {
17085 OSStatus err;
17086
17087 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
17088 require_noerr( err, exit );
17089
17090 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
17091 require_noerr( err, exit );
17092
17093 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
17094 require_noerr( err, exit );
17095
17096 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
17097 require_noerr( err, exit );
17098
17099 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
17100 require_noerr( err, exit );
17101
17102 err = HTTPHeader_Commit( inHeader );
17103 require_noerr( err, exit );
17104
17105 exit:
17106 return( err );
17107 }
17108
17109 //===========================================================================================================================
17110 // SSDPDiscoverPrintPrologue
17111 //===========================================================================================================================
17112
17113 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
17114 {
17115 const int receiveSecs = inContext->receiveSecs;
17116 const char * ifName;
17117 char ifNameBuf[ IF_NAMESIZE + 1 ];
17118 NetTransportType ifType;
17119
17120 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
17121
17122 ifType = kNetTransportType_Undefined;
17123 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
17124
17125 FPrintF( stdout, "Interface: %s/%d/%s\n",
17126 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
17127 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
17128 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
17129 FPrintF( stdout, "Receive duration: " );
17130 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
17131 else FPrintF( stdout, "∞\n" );
17132 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17133 }
17134
17135 //===========================================================================================================================
17136 // SSDPDiscoverReadHandler
17137 //===========================================================================================================================
17138
17139 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader );
17140
17141 static void SSDPDiscoverReadHandler( void *inContext )
17142 {
17143 OSStatus err;
17144 struct timeval now;
17145 SocketContext * const sockCtx = (SocketContext *) inContext;
17146 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
17147 HTTPHeader * const header = &context->header;
17148 sockaddr_ip fromAddr;
17149 size_t msgLen;
17150
17151 gettimeofday( &now, NULL );
17152
17153 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
17154 NULL, NULL, NULL, NULL );
17155 require_noerr( err, exit );
17156
17157 FPrintF( stdout, "---\n" );
17158 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
17159 FPrintF( stdout, "Source: %##a\n", &fromAddr );
17160 FPrintF( stdout, "Message size: %zu\n", msgLen );
17161 header->len = msgLen;
17162 if( _HTTPHeader_Validate( header ) )
17163 {
17164 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
17165 if( header->extraDataLen > 0 )
17166 {
17167 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
17168 }
17169 }
17170 else
17171 {
17172 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
17173 goto exit;
17174 }
17175
17176 exit:
17177 if( err ) exit( 1 );
17178 }
17179
17180 //===========================================================================================================================
17181 // _HTTPHeader_Validate
17182 //
17183 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17184 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17185 //
17186 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17187 //===========================================================================================================================
17188
17189 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader )
17190 {
17191 const char * src;
17192 const char * end;
17193
17194 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17195
17196 require( inHeader->len < sizeof( inHeader->buf ), exit );
17197 src = inHeader->buf;
17198 end = src + inHeader->len;
17199 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
17200 {
17201 src += 4;
17202 }
17203 else
17204 {
17205 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17206 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17207
17208 for( ;; )
17209 {
17210 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
17211 if( src >= end ) goto exit;
17212 ++src;
17213 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17214 {
17215 src += 2;
17216 break;
17217 }
17218 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
17219 {
17220 src += 1;
17221 break;
17222 }
17223 }
17224 }
17225 inHeader->extraDataPtr = src;
17226 inHeader->extraDataLen = (size_t)( end - src );
17227 inHeader->len = (size_t)( src - inHeader->buf );
17228 return( true );
17229
17230 exit:
17231 return( false );
17232 }
17233
17234 #if( TARGET_OS_DARWIN )
17235 //===========================================================================================================================
17236 // ResQueryCmd
17237 //===========================================================================================================================
17238
17239 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17240
17241 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
17242 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
17243 int,
17244 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17245 ( dname, class, type, answer, anslen ) );
17246
17247 // res_query() from libinfo
17248
17249 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
17250 SOFT_LINK_FUNCTION_EX( info, res_query,
17251 int,
17252 ( const char *dname, int class, int type, u_char *answer, int anslen ),
17253 ( dname, class, type, answer, anslen ) );
17254
17255 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
17256
17257 static void ResQueryCmd( void )
17258 {
17259 OSStatus err;
17260 res_query_f res_query_ptr;
17261 int n;
17262 uint16_t type, class;
17263 uint8_t answer[ 1024 ];
17264
17265 // Get pointer to one of the res_query() functions.
17266
17267 if( gResQuery_UseLibInfo )
17268 {
17269 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
17270 {
17271 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
17272 err = kNotFoundErr;
17273 goto exit;
17274 }
17275 res_query_ptr = soft_res_query;
17276 }
17277 else
17278 {
17279 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
17280 {
17281 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
17282 err = kNotFoundErr;
17283 goto exit;
17284 }
17285 res_query_ptr = soft_res_9_query;
17286 }
17287
17288 // Get record type.
17289
17290 err = RecordTypeFromArgString( gResQuery_Type, &type );
17291 require_noerr( err, exit );
17292
17293 // Get record class.
17294
17295 if( gResQuery_Class )
17296 {
17297 err = RecordClassFromArgString( gResQuery_Class, &class );
17298 require_noerr( err, exit );
17299 }
17300 else
17301 {
17302 class = kDNSServiceClass_IN;
17303 }
17304
17305 // Print prologue.
17306
17307 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
17308 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17309 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17310 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17311 FPrintF( stdout, "---\n" );
17312
17313 // Call res_query().
17314
17315 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
17316 if( n < 0 )
17317 {
17318 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17319 err = kUnknownErr;
17320 goto exit;
17321 }
17322
17323 // Print result.
17324
17325 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17326
17327 exit:
17328 if( err ) exit( 1 );
17329 }
17330
17331 //===========================================================================================================================
17332 // ResolvDNSQueryCmd
17333 //===========================================================================================================================
17334
17335 // 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
17336 // avoid including the header file.
17337
17338 typedef void * dns_handle_t;
17339
17340 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
17341 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
17342 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
17343 int32_t, (
17344 dns_handle_t dns,
17345 const char * name,
17346 uint32_t dnsclass,
17347 uint32_t dnstype,
17348 char * buf,
17349 uint32_t len,
17350 struct sockaddr * from,
17351 uint32_t * fromlen ),
17352 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
17353
17354 static void ResolvDNSQueryCmd( void )
17355 {
17356 OSStatus err;
17357 int n;
17358 dns_handle_t dns = NULL;
17359 uint16_t type, class;
17360 sockaddr_ip from;
17361 uint32_t fromLen;
17362 uint8_t answer[ 1024 ];
17363
17364 // Make sure that the required symbols are available.
17365
17366 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
17367 {
17368 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
17369 err = kNotFoundErr;
17370 goto exit;
17371 }
17372
17373 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
17374 {
17375 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
17376 err = kNotFoundErr;
17377 goto exit;
17378 }
17379
17380 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
17381 {
17382 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
17383 err = kNotFoundErr;
17384 goto exit;
17385 }
17386
17387 // Get record type.
17388
17389 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
17390 require_noerr( err, exit );
17391
17392 // Get record class.
17393
17394 if( gResolvDNSQuery_Class )
17395 {
17396 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
17397 require_noerr( err, exit );
17398 }
17399 else
17400 {
17401 class = kDNSServiceClass_IN;
17402 }
17403
17404 // Get dns handle.
17405
17406 dns = soft_dns_open( gResolvDNSQuery_Path );
17407 if( !dns )
17408 {
17409 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
17410 err = kUnknownErr;
17411 goto exit;
17412 }
17413
17414 // Print prologue.
17415
17416 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
17417 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
17418 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
17419 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
17420 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17421 FPrintF( stdout, "---\n" );
17422
17423 // Call dns_query().
17424
17425 memset( &from, 0, sizeof( from ) );
17426 fromLen = (uint32_t) sizeof( from );
17427 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
17428 &fromLen );
17429 if( n < 0 )
17430 {
17431 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
17432 err = kUnknownErr;
17433 goto exit;
17434 }
17435
17436 // Print result.
17437
17438 FPrintF( stdout, "From: %##a\n", &from );
17439 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
17440
17441 exit:
17442 if( dns ) soft_dns_free( dns );
17443 if( err ) exit( 1 );
17444 }
17445
17446 //===========================================================================================================================
17447 // CFHostCmd
17448 //===========================================================================================================================
17449
17450 static void
17451 _CFHostResolveCallback(
17452 CFHostRef inHost,
17453 CFHostInfoType inInfoType,
17454 const CFStreamError * inError,
17455 void * inInfo );
17456
17457 static void CFHostCmd( void )
17458 {
17459 OSStatus err;
17460 CFStringRef name;
17461 Boolean success;
17462 CFHostRef host = NULL;
17463 CFHostClientContext context;
17464 CFStreamError streamErr;
17465
17466 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
17467 require_action( name, exit, err = kUnknownErr );
17468
17469 host = CFHostCreateWithName( kCFAllocatorDefault, name );
17470 ForgetCF( &name );
17471 require_action( host, exit, err = kUnknownErr );
17472
17473 memset( &context, 0, sizeof( context ) );
17474 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
17475 require_action( success, exit, err = kUnknownErr );
17476
17477 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
17478
17479 // Print prologue.
17480
17481 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
17482 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17483 FPrintF( stdout, "---\n" );
17484
17485 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
17486 require_action( success, exit, err = kUnknownErr );
17487 err = kNoErr;
17488
17489 CFRunLoopRun();
17490
17491 exit:
17492 CFReleaseNullSafe( host );
17493 if( err ) exit( 1 );
17494 }
17495
17496 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
17497 {
17498 OSStatus err;
17499 struct timeval now;
17500
17501 gettimeofday( &now, NULL );
17502
17503 Unused( inInfoType );
17504 Unused( inInfo );
17505
17506 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
17507 {
17508 err = inError->error;
17509 if( inError->domain == kCFStreamErrorDomainNetDB )
17510 {
17511 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
17512 }
17513 else
17514 {
17515 FPrintF( stderr, "Error %#m\n", err );
17516 }
17517 }
17518 else
17519 {
17520 CFArrayRef addresses;
17521 CFIndex count, i;
17522 CFDataRef addrData;
17523 const struct sockaddr * sockAddr;
17524 Boolean wasResolved = false;
17525
17526 addresses = CFHostGetAddressing( inHost, &wasResolved );
17527 check( wasResolved );
17528
17529 if( addresses )
17530 {
17531 count = CFArrayGetCount( addresses );
17532 for( i = 0; i < count; ++i )
17533 {
17534 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
17535 require_noerr( err, exit );
17536
17537 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
17538 FPrintF( stdout, "%##a\n", sockAddr );
17539 }
17540 }
17541 err = kNoErr;
17542 }
17543
17544 FPrintF( stdout, "---\n" );
17545 FPrintF( stdout, "End time: %{du:time}\n", &now );
17546
17547 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
17548
17549 exit:
17550 exit( err ? 1 : 0 );
17551 }
17552
17553 //===========================================================================================================================
17554 // DNSConfigAddCmd
17555 //
17556 // Note: Based on ajn's supplemental test tool.
17557 //===========================================================================================================================
17558
17559 static void DNSConfigAddCmd( void )
17560 {
17561 OSStatus err;
17562 CFMutableDictionaryRef dict = NULL;
17563 CFMutableArrayRef array = NULL;
17564 size_t i;
17565 SCDynamicStoreRef store = NULL;
17566 CFStringRef key = NULL;
17567 Boolean success;
17568
17569 // Create dictionary.
17570
17571 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
17572 require_action( dict, exit, err = kNoMemoryErr );
17573
17574 // Add DNS server IP addresses.
17575
17576 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
17577 require_action( array, exit, err = kNoMemoryErr );
17578
17579 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
17580 {
17581 CFStringRef addrStr;
17582
17583 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
17584 require_action( addrStr, exit, err = kUnknownErr );
17585
17586 CFArrayAppendValue( array, addrStr );
17587 CFRelease( addrStr );
17588 }
17589
17590 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
17591 ForgetCF( &array );
17592
17593 // Add domains, if any.
17594
17595 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
17596 require_action( array, exit, err = kNoMemoryErr );
17597
17598 if( gDNSConfigAdd_DomainCount > 0 )
17599 {
17600 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
17601 {
17602 CFStringRef domainStr;
17603
17604 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
17605 require_action( domainStr, exit, err = kUnknownErr );
17606
17607 CFArrayAppendValue( array, domainStr );
17608 CFRelease( domainStr );
17609 }
17610 }
17611 else
17612 {
17613 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17614
17615 CFArrayAppendValue( array, CFSTR( "" ) );
17616 }
17617
17618 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
17619 ForgetCF( &array );
17620
17621 // Add interface, if any.
17622
17623 if( gDNSConfigAdd_Interface )
17624 {
17625 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
17626 require_noerr( err, exit );
17627
17628 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
17629 }
17630
17631 // Set dictionary in dynamic store.
17632
17633 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17634 err = map_scerror( store );
17635 require_noerr( err, exit );
17636
17637 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
17638 require_action( key, exit, err = kUnknownErr );
17639
17640 success = SCDynamicStoreSetValue( store, key, dict );
17641 require_action( success, exit, err = kUnknownErr );
17642
17643 exit:
17644 CFReleaseNullSafe( dict );
17645 CFReleaseNullSafe( array );
17646 CFReleaseNullSafe( store );
17647 CFReleaseNullSafe( key );
17648 gExitCode = err ? 1 : 0;
17649 }
17650
17651 //===========================================================================================================================
17652 // DNSConfigRemoveCmd
17653 //===========================================================================================================================
17654
17655 static void DNSConfigRemoveCmd( void )
17656 {
17657 OSStatus err;
17658 SCDynamicStoreRef store = NULL;
17659 CFStringRef key = NULL;
17660 Boolean success;
17661
17662 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
17663 err = map_scerror( store );
17664 require_noerr( err, exit );
17665
17666 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
17667 require_action( key, exit, err = kUnknownErr );
17668
17669 success = SCDynamicStoreRemoveValue( store, key );
17670 require_action( success, exit, err = kUnknownErr );
17671
17672 exit:
17673 CFReleaseNullSafe( store );
17674 CFReleaseNullSafe( key );
17675 gExitCode = err ? 1 : 0;
17676 }
17677
17678 //===========================================================================================================================
17679 // XPCSendCmd
17680 //===========================================================================================================================
17681
17682 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
17683
17684 static void XPCSendCmd( void )
17685 {
17686 OSStatus err;
17687 xpc_object_t msg, reply;
17688
17689 err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
17690 require_noerr_quiet( err, exit );
17691
17692 FPrintF( stdout, "Service: %s\n", gXPCSend_ServiceName );
17693 FPrintF( stdout, "Message: %s\n", gXPCSend_MessageStr );
17694 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
17695 FPrintF( stdout, "---\n" );
17696 FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
17697
17698 err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
17699 xpc_forget( &msg );
17700 require_noerr_quiet( err, exit );
17701
17702 FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
17703 FPrintF( stdout, "---\n" );
17704 FPrintF( stdout, "End time: %{du:time}\n", NULL );
17705 xpc_forget( &reply );
17706
17707 exit:
17708 if( err ) ErrQuit( 1, "error: %#m\n", err );
17709 }
17710
17711 //===========================================================================================================================
17712 // _XPCDictionaryCreateFromString
17713 //===========================================================================================================================
17714
17715 #define kXPCObjectPrefix_Bool "bool:"
17716 #define kXPCObjectPrefix_Data "data:"
17717 #define kXPCObjectPrefix_Int64 "int:"
17718 #define kXPCObjectPrefix_String "string:"
17719 #define kXPCObjectPrefix_UInt64 "uint:"
17720 #define kXPCObjectPrefix_UUID "uuid:"
17721
17722 typedef struct XPCListItem XPCListItem;
17723 struct XPCListItem
17724 {
17725 XPCListItem * next;
17726 xpc_object_t obj;
17727 char * key;
17728 };
17729
17730 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
17731 static void _XPCListItemFree( XPCListItem *inItem );
17732 static void _XPCListFree( XPCListItem *inList );
17733
17734 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject );
17735
17736 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
17737 {
17738 OSStatus err;
17739 xpc_object_t container;
17740 const char * ptr = inString;
17741 const char * const end = inString + strlen( inString );
17742 XPCListItem * list = NULL;
17743
17744 container = xpc_dictionary_create( NULL, NULL, 0 );
17745 require_action( container, exit, err = kNoMemoryErr );
17746
17747 while( *ptr )
17748 {
17749 xpc_type_t containerType;
17750 xpc_object_t value;
17751 int c;
17752 char keyStr[ 256 ];
17753 char valStr[ 256 ];
17754
17755 // At this point, zero or more of the current container's elements have been parsed.
17756 // Skip the white space leading up to the container's next element, if any, or the container's end.
17757
17758 while( isspace_safe( *ptr ) ) ++ptr;
17759
17760 // Check if we're done with the current container.
17761
17762 c = *ptr;
17763 if( c == '\0' ) break;
17764
17765 containerType = xpc_get_type( container );
17766 if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
17767 ( ( containerType == XPC_TYPE_ARRAY ) && ( c == ']' ) ) )
17768 {
17769 XPCListItem * item;
17770
17771 item = list;
17772 require_action_quiet( item, exit, err = kMalformedErr );
17773
17774 ++ptr;
17775
17776 // Add the current container to its parent container.
17777
17778 if( item->key )
17779 {
17780 xpc_dictionary_set_value( item->obj, item->key, container );
17781 }
17782 else
17783 {
17784 xpc_array_append_value( item->obj, container );
17785 }
17786
17787 // Continue with the parent container.
17788
17789 xpc_release( container );
17790 container = xpc_retain( item->obj );
17791 list = item->next;
17792 _XPCListItemFree( item );
17793 continue;
17794 }
17795
17796 // If the current container is a dictionary, parse the key string.
17797
17798 if( containerType == XPC_TYPE_DICTIONARY )
17799 {
17800 err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
17801 require_noerr_quiet( err, exit );
17802
17803 c = *ptr;
17804 require_action_quiet( c == '=', exit, err = kMalformedErr );
17805 ++ptr;
17806 }
17807
17808 // Check if the value is a dictionary ({...}) or an array ([...]).
17809
17810 c = *ptr;
17811 if( ( c == '{' ) || ( c == '[' ) )
17812 {
17813 XPCListItem * item;
17814
17815 ++ptr;
17816
17817 // Save the current container.
17818
17819 err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
17820 require_noerr( err, exit );
17821
17822 item->next = list;
17823 list = item;
17824 item = NULL;
17825
17826 // Create and continue with the child container.
17827
17828 xpc_release( container );
17829 if( c == '{' )
17830 {
17831 container = xpc_dictionary_create( NULL, NULL, 0 );
17832 require_action( container, exit, err = kNoMemoryErr );
17833 }
17834 else
17835 {
17836 container = xpc_array_create( NULL, 0 );
17837 require_action( container, exit, err = kNoMemoryErr );
17838 }
17839 continue;
17840 }
17841
17842 // Parse the value string.
17843
17844 err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
17845 require_noerr_quiet( err, exit );
17846
17847 err = _XPCObjectFromString( valStr, &value );
17848 require_noerr_quiet( err, exit );
17849
17850 if( containerType == XPC_TYPE_DICTIONARY )
17851 {
17852 xpc_dictionary_set_value( container, keyStr, value );
17853 }
17854 else
17855 {
17856 xpc_array_append_value( container, value );
17857 }
17858 xpc_forget( &value );
17859 }
17860 require_action_quiet( !list, exit, err = kMalformedErr );
17861
17862 check( container );
17863 check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
17864
17865 *outDict = container;
17866 container = NULL;
17867 err = kNoErr;
17868
17869 exit:
17870 xpc_release_null_safe( container );
17871 if( list ) _XPCListFree( list );
17872 return( err );
17873 }
17874
17875 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject )
17876 {
17877 OSStatus err;
17878 xpc_object_t object;
17879
17880 if( 0 ) {}
17881
17882 // Bool
17883
17884 else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
17885 {
17886 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Bool );
17887 bool value;
17888
17889 if( IsTrueString( str, kSizeCString ) )
17890 {
17891 value = true;
17892 }
17893 else if( IsFalseString( str, kSizeCString ) )
17894 {
17895 value = false;
17896 }
17897 else
17898 {
17899 err = kValueErr;
17900 goto exit;
17901 }
17902
17903 object = xpc_bool_create( value );
17904 require_action( object, exit, err = kNoMemoryErr );
17905 }
17906
17907 // Data
17908
17909 else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
17910 {
17911 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Data );
17912 uint8_t * dataPtr;
17913 size_t dataLen;
17914
17915 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
17916 require_noerr( err, exit );
17917
17918 object = xpc_data_create( dataPtr, dataLen );
17919 free( dataPtr );
17920 require_action( object, exit, err = kNoMemoryErr );
17921 }
17922
17923 // Int64
17924
17925 else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
17926 {
17927 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
17928 int64_t i64;
17929
17930 i64 = _StringToInt64( str, &err );
17931 require_noerr_quiet( err, exit );
17932
17933 object = xpc_int64_create( i64 );
17934 require_action( object, exit, err = kNoMemoryErr );
17935 }
17936
17937 // String
17938
17939 else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
17940 {
17941 const char * const str = inString + sizeof_string( kXPCObjectPrefix_String );
17942
17943 object = xpc_string_create( str );
17944 require_action( object, exit, err = kNoMemoryErr );
17945 }
17946
17947 // UInt64
17948
17949 else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
17950 {
17951 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
17952 uint64_t u64;
17953
17954 u64 = _StringToUInt64( str, &err );
17955 require_noerr_quiet( err, exit );
17956
17957 object = xpc_uint64_create( u64 );
17958 require_action( object, exit, err = kNoMemoryErr );
17959 }
17960
17961 // UUID
17962
17963 else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
17964 {
17965 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UUID );
17966 uuid_t uuid;
17967
17968 err = uuid_parse( str, uuid );
17969 require_noerr_action_quiet( err, exit, err = kValueErr );
17970
17971 object = xpc_uuid_create( uuid );
17972 require_action( object, exit, err = kNoMemoryErr );
17973 }
17974
17975 // Unsupported prefix
17976
17977 else
17978 {
17979 err = kValueErr;
17980 goto exit;
17981 }
17982
17983 *outObject = object;
17984 err = kNoErr;
17985
17986 exit:
17987 return( err );
17988 }
17989
17990 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
17991 {
17992 OSStatus err;
17993 XPCListItem * item;
17994
17995 item = (XPCListItem *) calloc( 1, sizeof( *item ) );
17996 require_action( item, exit, err = kNoMemoryErr );
17997
17998 item->obj = xpc_retain( inObject );
17999 if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
18000 {
18001 item->key = strdup( inKey );
18002 require_action( item->key, exit, err = kNoMemoryErr );
18003 }
18004
18005 *outItem = item;
18006 item = NULL;
18007 err = kNoErr;
18008
18009 exit:
18010 if( item ) _XPCListItemFree( item );
18011 return( err );
18012 }
18013
18014 static void _XPCListItemFree( XPCListItem *inItem )
18015 {
18016 xpc_forget( &inItem->obj );
18017 ForgetMem( &inItem->key );
18018 free( inItem );
18019 }
18020
18021 static void _XPCListFree( XPCListItem *inList )
18022 {
18023 XPCListItem * item;
18024
18025 while( ( item = inList ) != NULL )
18026 {
18027 inList = item->next;
18028 _XPCListItemFree( item );
18029 }
18030 }
18031 #endif // TARGET_OS_DARWIN
18032
18033 #if( MDNSRESPONDER_PROJECT )
18034 //===========================================================================================================================
18035 // InterfaceMonitorCmd
18036 //===========================================================================================================================
18037
18038 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
18039 static void _InterfaceMonitorSignalHandler( void *inContext );
18040
18041 static void InterfaceMonitorCmd( void )
18042 {
18043 OSStatus err;
18044 mdns_interface_monitor_t monitor;
18045 dispatch_source_t signalSource = NULL;
18046 uint32_t ifIndex;
18047 __block int exitCode;
18048
18049 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
18050 require_noerr_quiet( err, exit );
18051
18052 monitor = mdns_interface_monitor_create( ifIndex );
18053 require_action( monitor, exit, err = kNoResourcesErr );
18054
18055 exitCode = 0;
18056 mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
18057 mdns_interface_monitor_set_event_handler( monitor,
18058 ^( mdns_event_t inEvent, OSStatus inError )
18059 {
18060 switch( inEvent )
18061 {
18062 case mdns_event_error:
18063 FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
18064 mdns_interface_monitor_invalidate( monitor );
18065 exitCode = 1;
18066 break;
18067
18068 case mdns_event_invalidated:
18069 FPrintF( stdout, "Interface monitor invalidated.\n" );
18070 mdns_release( monitor );
18071 exit( exitCode );
18072
18073 default:
18074 FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
18075 break;
18076 }
18077 } );
18078 mdns_interface_monitor_set_update_handler( monitor,
18079 ^( __unused mdns_interface_flags_t inChangeFlags )
18080 {
18081 _InterfaceMonitorPrint( monitor );
18082 } );
18083
18084 _InterfaceMonitorPrint( monitor );
18085 mdns_interface_monitor_activate( monitor );
18086
18087 signal( SIGINT, SIG_IGN );
18088 err = DispatchSignalSourceCreate( SIGINT, _InterfaceMonitorSignalHandler, monitor, &signalSource );
18089 require_noerr( err, exit );
18090 dispatch_resume( signalSource );
18091
18092 dispatch_main();
18093
18094 exit:
18095 if( err ) ErrQuit( 1, "error: %#m\n", err );
18096 }
18097
18098 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
18099 {
18100 FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
18101 }
18102
18103 static void _InterfaceMonitorSignalHandler( void *inContext )
18104 {
18105 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
18106 }
18107
18108 //===========================================================================================================================
18109 // DNSProxyCmd
18110 //===========================================================================================================================
18111
18112 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
18113 static void _DNSProxyCmdSignalHandler( void *inContext );
18114
18115 static void DNSProxyCmd( void )
18116 {
18117 OSStatus err;
18118 size_t i;
18119 DNSXConnRef connection;
18120 IfIndex inputIfIndexes[ MaxInputIf ];
18121 dispatch_source_t sigIntSource = NULL;
18122 dispatch_source_t sigTermSource = NULL;
18123 uint32_t outputIfIndex;
18124 char ifName[ kInterfaceNameBufLen ];
18125
18126 if( gDNSProxy_InputInterfaceCount > MaxInputIf )
18127 {
18128 FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
18129 gDNSProxy_InputInterfaceCount, MaxInputIf );
18130 err = kRangeErr;
18131 goto exit;
18132 }
18133
18134 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18135 {
18136 uint32_t ifIndex;
18137
18138 err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
18139 require_noerr_quiet( err, exit );
18140
18141 inputIfIndexes[ i ] = ifIndex;
18142 }
18143 while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0; // Remaining interface indexes are required to be 0.
18144
18145 if( gDNSProxy_OutputInterface )
18146 {
18147 err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
18148 require_noerr_quiet( err, exit );
18149 }
18150 else
18151 {
18152 outputIfIndex = kDNSIfindexAny;
18153 }
18154
18155 FPrintF( stdout, "Input Interfaces:" );
18156 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
18157 {
18158 const uint32_t ifIndex = (uint32_t) inputIfIndexes[ i ];
18159
18160 FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
18161 }
18162 FPrintF( stdout, "\n" );
18163 FPrintF( stdout, "Output Interface: %u (%s)\n", outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
18164 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
18165 FPrintF( stdout, "---\n" );
18166
18167 connection = NULL;
18168 err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex, dispatch_get_main_queue(),
18169 _DNSProxyCallback );
18170 require_noerr_quiet( err, exit );
18171
18172 signal( SIGINT, SIG_IGN );
18173 err = DispatchSignalSourceCreate( SIGINT, _DNSProxyCmdSignalHandler, connection, &sigIntSource );
18174 require_noerr( err, exit );
18175 dispatch_activate( sigIntSource );
18176
18177 signal( SIGTERM, SIG_IGN );
18178 err = DispatchSignalSourceCreate( SIGTERM, _DNSProxyCmdSignalHandler, connection, &sigTermSource );
18179 require_noerr( err, exit );
18180 dispatch_activate( sigTermSource );
18181
18182 dispatch_main();
18183
18184 exit:
18185 if( err ) ErrQuit( 1, "error: %#m\n", err );
18186 }
18187
18188 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
18189 {
18190 Unused( inConnection );
18191
18192 if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
18193 }
18194
18195 static void _DNSProxyCmdSignalHandler( void *inContext )
18196 {
18197 DNSXConnRef const connection = (DNSXConnRef) inContext;
18198 struct timeval now;
18199
18200 gettimeofday( &now, NULL );
18201
18202 DNSXRefDeAlloc( connection );
18203
18204 FPrintF( stdout, "---\n" );
18205 FPrintF( stdout, "End time: %{du:time}\n", &now );
18206 exit( 0 );
18207 }
18208
18209 //===========================================================================================================================
18210 // XCTestCmd
18211 //===========================================================================================================================
18212
18213 static void XCTestCmd( void )
18214 {
18215 int result = 0;
18216 setenv(DNSSDUTIL_XCTEST, DNSSDUTIL_XCTEST, 0);
18217 if(!TestUtilsRunXCTestNamed(gXCTest_Classname)) {
18218 result = 1;
18219 }
18220 unsetenv(DNSSDUTIL_XCTEST);
18221 exit( result );
18222 }
18223
18224 #endif // MDNSRESPONDER_PROJECT
18225
18226 //===========================================================================================================================
18227 // DaemonVersionCmd
18228 //===========================================================================================================================
18229
18230 static void DaemonVersionCmd( void )
18231 {
18232 OSStatus err;
18233 uint32_t size, version;
18234 char strBuf[ 16 ];
18235
18236 size = (uint32_t) sizeof( version );
18237 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
18238 require_noerr( err, exit );
18239
18240 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
18241
18242 exit:
18243 if( err ) exit( 1 );
18244 }
18245
18246 //===========================================================================================================================
18247 // Exit
18248 //===========================================================================================================================
18249
18250 static void Exit( void *inContext )
18251 {
18252 const char * const reason = (const char *) inContext;
18253
18254 FPrintF( stdout, "---\n" );
18255 FPrintF( stdout, "End time: %{du:time}\n", NULL );
18256 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
18257 exit( gExitCode );
18258 }
18259
18260 //===========================================================================================================================
18261 // _PrintFExtensionTimestampHandler
18262 //===========================================================================================================================
18263
18264 static int
18265 _PrintFExtensionTimestampHandler(
18266 PrintFContext * inContext,
18267 PrintFFormat * inFormat,
18268 PrintFVAList * inArgs,
18269 void * inUserContext )
18270 {
18271 struct timeval now;
18272 const struct timeval * tv;
18273 struct tm * localTime;
18274 size_t len;
18275 int n;
18276 char dateTimeStr[ 32 ];
18277
18278 Unused( inUserContext );
18279
18280 tv = va_arg( inArgs->args, const struct timeval * );
18281 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18282
18283 if( !tv )
18284 {
18285 gettimeofday( &now, NULL );
18286 tv = &now;
18287 }
18288 localTime = localtime( &tv->tv_sec );
18289 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
18290 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
18291
18292 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
18293
18294 exit:
18295 return( n );
18296 }
18297
18298 //===========================================================================================================================
18299 // _PrintFExtensionDNSMessageHandler
18300 //===========================================================================================================================
18301
18302 static int
18303 _PrintFExtensionDNSMessageHandler(
18304 PrintFContext * inContext,
18305 PrintFFormat * inFormat,
18306 PrintFVAList * inArgs,
18307 void * inUserContext )
18308 {
18309 OSStatus err;
18310 const void * msgPtr;
18311 size_t msgLen;
18312 char * text;
18313 int n;
18314 Boolean isMDNS;
18315 Boolean printRawRData;
18316
18317 Unused( inUserContext );
18318
18319 msgPtr = va_arg( inArgs->args, const void * );
18320 msgLen = va_arg( inArgs->args, size_t );
18321 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18322
18323 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
18324 printRawRData = ( inFormat->precision > 0 ) ? true : false;
18325 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
18326 if( !err )
18327 {
18328 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
18329 free( text );
18330 }
18331 else
18332 {
18333 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
18334 }
18335
18336 exit:
18337 return( n );
18338 }
18339
18340 //===========================================================================================================================
18341 // _PrintFExtensionCallbackFlagsHandler
18342 //===========================================================================================================================
18343
18344 static int
18345 _PrintFExtensionCallbackFlagsHandler(
18346 PrintFContext * inContext,
18347 PrintFFormat * inFormat,
18348 PrintFVAList * inArgs,
18349 void * inUserContext )
18350 {
18351 DNSServiceFlags flags;
18352 int n;
18353
18354 Unused( inUserContext );
18355
18356 flags = va_arg( inArgs->args, DNSServiceFlags );
18357 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18358
18359 n = PrintFCore( inContext, "%08X %s%c %c%c",
18360 flags, DNSServiceFlagsToAddRmvStr( flags ),
18361 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
18362 ( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
18363 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '*' : ' ' );
18364
18365 exit:
18366 return( n );
18367 }
18368
18369 //===========================================================================================================================
18370 // _PrintFExtensionDNSRecordDataHandler
18371 //===========================================================================================================================
18372
18373 static int
18374 _PrintFExtensionDNSRecordDataHandler(
18375 PrintFContext * inContext,
18376 PrintFFormat * inFormat,
18377 PrintFVAList * inArgs,
18378 void * inUserContext )
18379 {
18380 const void * rdataPtr;
18381 unsigned int rdataLen, rdataType;
18382 int n, fieldWidth;
18383
18384 Unused( inUserContext );
18385
18386 rdataType = va_arg( inArgs->args, unsigned int );
18387 rdataPtr = va_arg( inArgs->args, const void * );
18388 rdataLen = va_arg( inArgs->args, unsigned int );
18389 require_action_quiet( !inFormat->suppress, exit, n = 0 );
18390
18391 check( inFormat->fieldWidth < INT_MAX );
18392 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
18393
18394 if( rdataLen > 0 )
18395 {
18396 char * rdataStr = NULL;
18397
18398 DNSRecordDataToString( rdataPtr, rdataLen, rdataType, NULL, 0, &rdataStr );
18399 if( rdataStr )
18400 {
18401 n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
18402 free( rdataStr );
18403 }
18404 else
18405 {
18406 n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
18407 }
18408 }
18409 else
18410 {
18411 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
18412 }
18413
18414 exit:
18415 return( n );
18416 }
18417
18418 //===========================================================================================================================
18419 // GetDNSSDFlagsFromOpts
18420 //===========================================================================================================================
18421
18422 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
18423 {
18424 DNSServiceFlags flags;
18425
18426 flags = (DNSServiceFlags) gDNSSDFlags;
18427 if( flags & kDNSServiceFlagsShareConnection )
18428 {
18429 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18430 kDNSServiceFlagsShareConnection );
18431 }
18432
18433 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
18434 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
18435 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
18436 if( gDNSSDFlag_DenyConstrained ) flags |= kDNSServiceFlagsDenyConstrained;
18437 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
18438 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
18439 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
18440 if( gDNSSDFlag_KnownUnique ) flags |= kDNSServiceFlagsKnownUnique;
18441 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
18442 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
18443 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
18444 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
18445 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
18446 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
18447 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
18448 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
18449 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
18450 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
18451
18452 return( flags );
18453 }
18454
18455 //===========================================================================================================================
18456 // CreateConnectionFromArgString
18457 //===========================================================================================================================
18458
18459 static OSStatus
18460 CreateConnectionFromArgString(
18461 const char * inString,
18462 dispatch_queue_t inQueue,
18463 DNSServiceRef * outSDRef,
18464 ConnectionDesc * outDesc )
18465 {
18466 OSStatus err;
18467 DNSServiceRef sdRef = NULL;
18468 ConnectionType type;
18469 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
18470 uint8_t uuid[ 16 ];
18471
18472 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
18473 {
18474 err = DNSServiceCreateConnection( &sdRef );
18475 require_noerr( err, exit );
18476 type = kConnectionType_Normal;
18477 }
18478 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
18479 {
18480 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
18481
18482 err = StringToInt32( pidStr, &pid );
18483 if( err )
18484 {
18485 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
18486 err = kParamErr;
18487 goto exit;
18488 }
18489
18490 memset( uuid, 0, sizeof( uuid ) );
18491 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
18492 if( err )
18493 {
18494 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
18495 goto exit;
18496 }
18497 type = kConnectionType_DelegatePID;
18498 }
18499 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
18500 {
18501 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
18502
18503 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
18504
18505 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
18506 if( err )
18507 {
18508 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
18509 err = kParamErr;
18510 goto exit;
18511 }
18512
18513 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
18514 if( err )
18515 {
18516 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
18517 goto exit;
18518 }
18519 type = kConnectionType_DelegateUUID;
18520 }
18521 else
18522 {
18523 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
18524 err = kParamErr;
18525 goto exit;
18526 }
18527
18528 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
18529 require_noerr( err, exit );
18530
18531 *outSDRef = sdRef;
18532 if( outDesc )
18533 {
18534 outDesc->type = type;
18535 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
18536 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
18537 }
18538 sdRef = NULL;
18539
18540 exit:
18541 if( sdRef ) DNSServiceRefDeallocate( sdRef );
18542 return( err );
18543 }
18544
18545 //===========================================================================================================================
18546 // InterfaceIndexFromArgString
18547 //===========================================================================================================================
18548
18549 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
18550 {
18551 OSStatus err;
18552 uint32_t ifIndex;
18553
18554 if( inString )
18555 {
18556 ifIndex = if_nametoindex( inString );
18557 if( ifIndex == 0 )
18558 {
18559 err = StringToUInt32( inString, &ifIndex );
18560 if( err )
18561 {
18562 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
18563 err = kParamErr;
18564 goto exit;
18565 }
18566 }
18567 }
18568 else
18569 {
18570 ifIndex = 0;
18571 }
18572
18573 *outIndex = ifIndex;
18574 err = kNoErr;
18575
18576 exit:
18577 return( err );
18578 }
18579
18580 //===========================================================================================================================
18581 // RecordDataFromArgString
18582 //===========================================================================================================================
18583
18584 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
18585 {
18586 OSStatus err;
18587 uint8_t * dataPtr = NULL;
18588 size_t dataLen;
18589
18590 if( 0 ) {}
18591
18592 // Domain name
18593
18594 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
18595 {
18596 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
18597
18598 err = StringToDomainName( str, &dataPtr, &dataLen );
18599 require_noerr_quiet( err, exit );
18600 }
18601
18602 // File path
18603
18604 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
18605 {
18606 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
18607
18608 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
18609 require_noerr( err, exit );
18610 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18611 }
18612
18613 // Hexadecimal string
18614
18615 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
18616 {
18617 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
18618
18619 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
18620 require_noerr( err, exit );
18621 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18622 }
18623
18624 // IPv4 address string
18625
18626 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
18627 {
18628 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
18629
18630 err = StringToARecordData( str, &dataPtr, &dataLen );
18631 require_noerr_quiet( err, exit );
18632 }
18633
18634 // IPv6 address string
18635
18636 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
18637 {
18638 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
18639
18640 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
18641 require_noerr_quiet( err, exit );
18642 }
18643
18644 // SRV record
18645
18646 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
18647 {
18648 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
18649
18650 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
18651 require_noerr( err, exit );
18652 }
18653
18654 // String with escaped hex and octal bytes
18655
18656 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
18657 {
18658 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
18659 const char * const end = str + strlen( str );
18660 size_t copiedLen;
18661 size_t totalLen;
18662 Boolean success;
18663
18664 if( str < end )
18665 {
18666 success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
18667 require_action( success, exit, err = kParamErr );
18668 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
18669
18670 dataLen = totalLen;
18671 dataPtr = (uint8_t *) malloc( dataLen );
18672 require_action( dataPtr, exit, err = kNoMemoryErr );
18673
18674 success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
18675 require_action( success, exit, err = kParamErr );
18676 check( copiedLen == dataLen );
18677 }
18678 else
18679 {
18680 dataPtr = NULL;
18681 dataLen = 0;
18682 }
18683 }
18684
18685 // TXT record
18686
18687 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
18688 {
18689 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
18690
18691 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
18692 require_noerr( err, exit );
18693 }
18694
18695 // Unrecognized format
18696
18697 else
18698 {
18699 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
18700 err = kParamErr;
18701 goto exit;
18702 }
18703
18704 err = kNoErr;
18705 *outDataLen = dataLen;
18706 *outDataPtr = dataPtr;
18707 dataPtr = NULL;
18708
18709 exit:
18710 FreeNullSafe( dataPtr );
18711 return( err );
18712 }
18713
18714 //===========================================================================================================================
18715 // RecordTypeFromArgString
18716 //===========================================================================================================================
18717
18718 typedef struct
18719 {
18720 uint16_t value; // Record type's numeric value.
18721 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18722
18723 } RecordType;
18724
18725 static const RecordType kRecordTypes[] =
18726 {
18727 // Common types.
18728
18729 { kDNSServiceType_A, "A" },
18730 { kDNSServiceType_AAAA, "AAAA" },
18731 { kDNSServiceType_PTR, "PTR" },
18732 { kDNSServiceType_SRV, "SRV" },
18733 { kDNSServiceType_TXT, "TXT" },
18734 { kDNSServiceType_CNAME, "CNAME" },
18735 { kDNSServiceType_SOA, "SOA" },
18736 { kDNSServiceType_NSEC, "NSEC" },
18737 { kDNSServiceType_NS, "NS" },
18738 { kDNSServiceType_MX, "MX" },
18739 { kDNSServiceType_ANY, "ANY" },
18740 { kDNSServiceType_OPT, "OPT" },
18741
18742 // Less common types.
18743
18744 { kDNSServiceType_MD, "MD" },
18745 { kDNSServiceType_NS, "NS" },
18746 { kDNSServiceType_MD, "MD" },
18747 { kDNSServiceType_MF, "MF" },
18748 { kDNSServiceType_MB, "MB" },
18749 { kDNSServiceType_MG, "MG" },
18750 { kDNSServiceType_MR, "MR" },
18751 { kDNSServiceType_NULL, "NULL" },
18752 { kDNSServiceType_WKS, "WKS" },
18753 { kDNSServiceType_HINFO, "HINFO" },
18754 { kDNSServiceType_MINFO, "MINFO" },
18755 { kDNSServiceType_RP, "RP" },
18756 { kDNSServiceType_AFSDB, "AFSDB" },
18757 { kDNSServiceType_X25, "X25" },
18758 { kDNSServiceType_ISDN, "ISDN" },
18759 { kDNSServiceType_RT, "RT" },
18760 { kDNSServiceType_NSAP, "NSAP" },
18761 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
18762 { kDNSServiceType_SIG, "SIG" },
18763 { kDNSServiceType_KEY, "KEY" },
18764 { kDNSServiceType_PX, "PX" },
18765 { kDNSServiceType_GPOS, "GPOS" },
18766 { kDNSServiceType_LOC, "LOC" },
18767 { kDNSServiceType_NXT, "NXT" },
18768 { kDNSServiceType_EID, "EID" },
18769 { kDNSServiceType_NIMLOC, "NIMLOC" },
18770 { kDNSServiceType_ATMA, "ATMA" },
18771 { kDNSServiceType_NAPTR, "NAPTR" },
18772 { kDNSServiceType_KX, "KX" },
18773 { kDNSServiceType_CERT, "CERT" },
18774 { kDNSServiceType_A6, "A6" },
18775 { kDNSServiceType_DNAME, "DNAME" },
18776 { kDNSServiceType_SINK, "SINK" },
18777 { kDNSServiceType_APL, "APL" },
18778 { kDNSServiceType_DS, "DS" },
18779 { kDNSServiceType_SSHFP, "SSHFP" },
18780 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
18781 { kDNSServiceType_RRSIG, "RRSIG" },
18782 { kDNSServiceType_DNSKEY, "DNSKEY" },
18783 { kDNSServiceType_DHCID, "DHCID" },
18784 { kDNSServiceType_NSEC3, "NSEC3" },
18785 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
18786 { kDNSServiceType_HIP, "HIP" },
18787 { kDNSServiceType_SPF, "SPF" },
18788 { kDNSServiceType_UINFO, "UINFO" },
18789 { kDNSServiceType_UID, "UID" },
18790 { kDNSServiceType_GID, "GID" },
18791 { kDNSServiceType_UNSPEC, "UNSPEC" },
18792 { kDNSServiceType_TKEY, "TKEY" },
18793 { kDNSServiceType_TSIG, "TSIG" },
18794 { kDNSServiceType_IXFR, "IXFR" },
18795 { kDNSServiceType_AXFR, "AXFR" },
18796 { kDNSServiceType_MAILB, "MAILB" },
18797 { kDNSServiceType_MAILA, "MAILA" }
18798 };
18799
18800 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
18801 {
18802 OSStatus err;
18803 int32_t i32;
18804 const RecordType * type;
18805 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18806
18807 for( type = kRecordTypes; type < end; ++type )
18808 {
18809 if( strcasecmp( type->name, inString ) == 0 )
18810 {
18811 *outValue = type->value;
18812 return( kNoErr );
18813 }
18814 }
18815
18816 err = StringToInt32( inString, &i32 );
18817 require_noerr_quiet( err, exit );
18818 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18819
18820 *outValue = (uint16_t) i32;
18821
18822 exit:
18823 return( err );
18824 }
18825
18826 //===========================================================================================================================
18827 // RecordClassFromArgString
18828 //===========================================================================================================================
18829
18830 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
18831 {
18832 OSStatus err;
18833 int32_t i32;
18834
18835 if( strcasecmp( inString, "IN" ) == 0 )
18836 {
18837 *outValue = kDNSServiceClass_IN;
18838 err = kNoErr;
18839 goto exit;
18840 }
18841
18842 err = StringToInt32( inString, &i32 );
18843 require_noerr_quiet( err, exit );
18844 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
18845
18846 *outValue = (uint16_t) i32;
18847
18848 exit:
18849 return( err );
18850 }
18851
18852 //===========================================================================================================================
18853 // InterfaceIndexToName
18854 //===========================================================================================================================
18855
18856 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
18857 {
18858 switch( inIfIndex )
18859 {
18860 case kDNSServiceInterfaceIndexAny:
18861 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
18862 break;
18863
18864 case kDNSServiceInterfaceIndexLocalOnly:
18865 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
18866 break;
18867
18868 case kDNSServiceInterfaceIndexUnicast:
18869 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
18870 break;
18871
18872 case kDNSServiceInterfaceIndexP2P:
18873 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
18874 break;
18875
18876 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18877 case kDNSServiceInterfaceIndexBLE:
18878 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
18879 break;
18880 #endif
18881
18882 default:
18883 {
18884 const char * name;
18885
18886 name = if_indextoname( inIfIndex, inNameBuf );
18887 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
18888 break;
18889 }
18890 }
18891
18892 return( inNameBuf );
18893 }
18894
18895 //===========================================================================================================================
18896 // RecordTypeToString
18897 //===========================================================================================================================
18898
18899 static const char * RecordTypeToString( unsigned int inValue )
18900 {
18901 const RecordType * type;
18902 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
18903
18904 for( type = kRecordTypes; type < end; ++type )
18905 {
18906 if( type->value == inValue ) return( type->name );
18907 }
18908 return( "???" );
18909 }
18910
18911 //===========================================================================================================================
18912 // DNSRecordDataToString
18913 //===========================================================================================================================
18914
18915 static OSStatus
18916 DNSRecordDataToString(
18917 const void * inRDataPtr,
18918 size_t inRDataLen,
18919 unsigned int inRDataType,
18920 const void * inMsgPtr,
18921 size_t inMsgLen,
18922 char ** outString )
18923 {
18924 OSStatus err;
18925 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
18926 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
18927 char * rdataStr;
18928 const uint8_t * ptr;
18929 int n;
18930 char domainNameStr[ kDNSServiceMaxDomainName ];
18931
18932 rdataStr = NULL;
18933
18934 // A Record
18935
18936 if( inRDataType == kDNSServiceType_A )
18937 {
18938 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
18939
18940 ASPrintF( &rdataStr, "%.4a", rdataPtr );
18941 require_action( rdataStr, exit, err = kNoMemoryErr );
18942 }
18943
18944 // AAAA Record
18945
18946 else if( inRDataType == kDNSServiceType_AAAA )
18947 {
18948 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
18949
18950 ASPrintF( &rdataStr, "%.16a", rdataPtr );
18951 require_action( rdataStr, exit, err = kNoMemoryErr );
18952 }
18953
18954 // PTR, CNAME, or NS Record
18955
18956 else if( ( inRDataType == kDNSServiceType_PTR ) ||
18957 ( inRDataType == kDNSServiceType_CNAME ) ||
18958 ( inRDataType == kDNSServiceType_NS ) )
18959 {
18960 if( inMsgPtr )
18961 {
18962 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
18963 require_noerr( err, exit );
18964 }
18965 else
18966 {
18967 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
18968 require_noerr( err, exit );
18969 }
18970
18971 rdataStr = strdup( domainNameStr );
18972 require_action( rdataStr, exit, err = kNoMemoryErr );
18973 }
18974
18975 // SRV Record
18976
18977 else if( inRDataType == kDNSServiceType_SRV )
18978 {
18979 const dns_fixed_fields_srv * fields;
18980 const uint8_t * target;
18981 unsigned int priority, weight, port;
18982
18983 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
18984
18985 fields = (const dns_fixed_fields_srv *) rdataPtr;
18986 priority = dns_fixed_fields_srv_get_priority( fields );
18987 weight = dns_fixed_fields_srv_get_weight( fields );
18988 port = dns_fixed_fields_srv_get_port( fields );
18989 target = (const uint8_t *) &fields[ 1 ];
18990
18991 if( inMsgPtr )
18992 {
18993 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
18994 require_noerr( err, exit );
18995 }
18996 else
18997 {
18998 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
18999 require_noerr( err, exit );
19000 }
19001
19002 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
19003 require_action( rdataStr, exit, err = kNoMemoryErr );
19004 }
19005
19006 // TXT Record
19007
19008 else if( inRDataType == kDNSServiceType_TXT )
19009 {
19010 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
19011
19012 if( inRDataLen == 1 )
19013 {
19014 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
19015 require_action( rdataStr, exit, err = kNoMemoryErr );
19016 }
19017 else
19018 {
19019 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
19020 require_action( rdataStr, exit, err = kNoMemoryErr );
19021 }
19022 }
19023
19024 // SOA Record
19025
19026 else if( inRDataType == kDNSServiceType_SOA )
19027 {
19028 const dns_fixed_fields_soa * fields;
19029 uint32_t serial, refresh, retry, expire, minimum;
19030
19031 if( inMsgPtr )
19032 {
19033 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19034 require_noerr( err, exit );
19035
19036 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19037
19038 rdataStr = strdup( domainNameStr );
19039 require_action( rdataStr, exit, err = kNoMemoryErr );
19040
19041 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
19042 require_noerr( err, exit );
19043 }
19044 else
19045 {
19046 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19047 require_noerr( err, exit );
19048
19049 rdataStr = strdup( domainNameStr );
19050 require_action( rdataStr, exit, err = kNoMemoryErr );
19051
19052 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
19053 require_noerr( err, exit );
19054 }
19055
19056 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
19057
19058 fields = (const dns_fixed_fields_soa *) ptr;
19059 serial = dns_fixed_fields_soa_get_serial( fields );
19060 refresh = dns_fixed_fields_soa_get_refresh( fields );
19061 retry = dns_fixed_fields_soa_get_retry( fields );
19062 expire = dns_fixed_fields_soa_get_expire( fields );
19063 minimum = dns_fixed_fields_soa_get_minimum( fields );
19064
19065 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
19066 require_action( n > 0, exit, err = kUnknownErr );
19067 }
19068
19069 // NSEC Record
19070
19071 else if( inRDataType == kDNSServiceType_NSEC )
19072 {
19073 unsigned int windowBlock, bitmapLen, i, recordType;
19074 const uint8_t * bitmapPtr;
19075
19076 if( inMsgPtr )
19077 {
19078 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
19079 require_noerr( err, exit );
19080 }
19081 else
19082 {
19083 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
19084 require_noerr( err, exit );
19085 }
19086
19087 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
19088
19089 rdataStr = strdup( domainNameStr );
19090 require_action( rdataStr, exit, err = kNoMemoryErr );
19091
19092 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
19093 {
19094 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19095
19096 windowBlock = ptr[ 0 ];
19097 bitmapLen = ptr[ 1 ];
19098 bitmapPtr = &ptr[ 2 ];
19099
19100 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
19101 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
19102
19103 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
19104 {
19105 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
19106 {
19107 recordType = ( windowBlock * 256 ) + i;
19108 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
19109 require_action( n > 0, exit, err = kUnknownErr );
19110 }
19111 }
19112 }
19113 }
19114
19115 // MX Record
19116
19117 else if( inRDataType == kDNSServiceType_MX )
19118 {
19119 uint16_t preference;
19120 const uint8_t * exchange;
19121
19122 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
19123
19124 preference = ReadBig16( rdataPtr );
19125 exchange = &rdataPtr[ 2 ];
19126
19127 if( inMsgPtr )
19128 {
19129 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
19130 require_noerr( err, exit );
19131 }
19132 else
19133 {
19134 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
19135 require_noerr( err, exit );
19136 }
19137
19138 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
19139 require_action( n > 0, exit, err = kUnknownErr );
19140 }
19141
19142 // Unhandled record type
19143
19144 else
19145 {
19146 err = kNotHandledErr;
19147 goto exit;
19148 }
19149
19150 check( rdataStr );
19151 *outString = rdataStr;
19152 rdataStr = NULL;
19153 err = kNoErr;
19154
19155 exit:
19156 FreeNullSafe( rdataStr );
19157 return( err );
19158 }
19159
19160 //===========================================================================================================================
19161 // DNSMessageToText
19162 //===========================================================================================================================
19163
19164 #define DNSFlagsOpCodeToString( X ) ( \
19165 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19166 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19167 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19168 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19169 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19170 "Unassigned" )
19171
19172 #define DNSFlagsRCodeToString( X ) ( \
19173 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19174 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19175 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19176 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19177 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19178 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19179 "???" )
19180
19181 static OSStatus
19182 DNSMessageToText(
19183 const uint8_t * inMsgPtr,
19184 size_t inMsgLen,
19185 const Boolean inMDNS,
19186 const Boolean inPrintRaw,
19187 char ** outText )
19188 {
19189 OSStatus err;
19190 DataBuffer dataBuf;
19191 size_t len;
19192 const DNSHeader * hdr;
19193 const uint8_t * ptr;
19194 unsigned int id, flags, opcode, rcode;
19195 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
19196 uint8_t name[ kDomainNameLengthMax ];
19197 char nameStr[ kDNSServiceMaxDomainName ];
19198
19199 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
19200 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19201
19202 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
19203
19204 hdr = (DNSHeader *) inMsgPtr;
19205 id = DNSHeaderGetID( hdr );
19206 flags = DNSHeaderGetFlags( hdr );
19207 questionCount = DNSHeaderGetQuestionCount( hdr );
19208 answerCount = DNSHeaderGetAnswerCount( hdr );
19209 authorityCount = DNSHeaderGetAuthorityCount( hdr );
19210 additionalCount = DNSHeaderGetAdditionalCount( hdr );
19211 opcode = DNSFlagsGetOpCode( flags );
19212 rcode = DNSFlagsGetRCode( flags );
19213
19214 _Append( "ID: 0x%04X (%u)\n", id, id );
19215 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19216 flags,
19217 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
19218 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
19219 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
19220 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
19221 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
19222 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
19223 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
19224 DNSFlagsRCodeToString( rcode ) );
19225 _Append( "Question count: %u\n", questionCount );
19226 _Append( "Answer count: %u\n", answerCount );
19227 _Append( "Authority count: %u\n", authorityCount );
19228 _Append( "Additional count: %u\n", additionalCount );
19229
19230 ptr = (const uint8_t *) &hdr[ 1 ];
19231 for( i = 0; i < questionCount; ++i )
19232 {
19233 uint16_t qtype, qclass;
19234 Boolean isQU;
19235
19236 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
19237 require_noerr( err, exit );
19238
19239 err = DomainNameToString( name, NULL, nameStr, NULL );
19240 require_noerr( err, exit );
19241
19242 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
19243 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
19244
19245 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
19246
19247 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19248 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
19249 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
19250 }
19251
19252 totalRRCount = answerCount + authorityCount + additionalCount;
19253 for( i = 0; i < totalRRCount; ++i )
19254 {
19255 uint16_t type;
19256 uint16_t class;
19257 uint32_t ttl;
19258 const uint8_t * rdataPtr;
19259 size_t rdataLen;
19260 char * rdataStr;
19261 Boolean cacheFlush;
19262
19263 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
19264 require_noerr( err, exit );
19265
19266 err = DomainNameToString( name, NULL, nameStr, NULL );
19267 require_noerr( err, exit );
19268
19269 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
19270 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
19271
19272 rdataStr = NULL;
19273 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
19274 if( !rdataStr )
19275 {
19276 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, (int) rdataLen );
19277 require_action( rdataStr, exit, err = kNoMemoryErr );
19278 }
19279
19280 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
19281 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
19282 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
19283
19284 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19285 nameStr, ttl, cacheFlush ? "CF" : "",
19286 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
19287 RecordTypeToString( type ), rdataStr );
19288 free( rdataStr );
19289 }
19290 _Append( "\n" );
19291
19292 err = DataBuffer_Append( &dataBuf, "", 1 );
19293 require_noerr( err, exit );
19294
19295 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
19296 require_noerr( err, exit );
19297
19298 exit:
19299 DataBuffer_Free( &dataBuf );
19300 return( err );
19301 }
19302
19303 //===========================================================================================================================
19304 // WriteDNSQueryMessage
19305 //===========================================================================================================================
19306
19307 static OSStatus
19308 WriteDNSQueryMessage(
19309 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
19310 uint16_t inMsgID,
19311 uint16_t inFlags,
19312 const char * inQName,
19313 uint16_t inQType,
19314 uint16_t inQClass,
19315 size_t * outMsgLen )
19316 {
19317 OSStatus err;
19318 uint8_t qname[ kDomainNameLengthMax ];
19319
19320 err = DomainNameFromString( qname, inQName, NULL );
19321 require_noerr_quiet( err, exit );
19322
19323 err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
19324 require_noerr_quiet( err, exit );
19325
19326 exit:
19327 return( err );
19328 }
19329
19330 //===========================================================================================================================
19331 // DispatchSignalSourceCreate
19332 //===========================================================================================================================
19333
19334 static OSStatus
19335 DispatchSignalSourceCreate(
19336 int inSignal,
19337 DispatchHandler inEventHandler,
19338 void * inContext,
19339 dispatch_source_t * outSource )
19340 {
19341 OSStatus err;
19342 dispatch_source_t source;
19343
19344 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
19345 require_action( source, exit, err = kUnknownErr );
19346
19347 dispatch_set_context( source, inContext );
19348 dispatch_source_set_event_handler_f( source, inEventHandler );
19349
19350 *outSource = source;
19351 err = kNoErr;
19352
19353 exit:
19354 return( err );
19355 }
19356
19357 //===========================================================================================================================
19358 // DispatchSocketSourceCreate
19359 //===========================================================================================================================
19360
19361 static OSStatus
19362 DispatchSocketSourceCreate(
19363 SocketRef inSock,
19364 dispatch_source_type_t inType,
19365 dispatch_queue_t inQueue,
19366 DispatchHandler inEventHandler,
19367 DispatchHandler inCancelHandler,
19368 void * inContext,
19369 dispatch_source_t * outSource )
19370 {
19371 OSStatus err;
19372 dispatch_source_t source;
19373
19374 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19375 require_action( source, exit, err = kUnknownErr );
19376
19377 dispatch_set_context( source, inContext );
19378 dispatch_source_set_event_handler_f( source, inEventHandler );
19379 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
19380
19381 *outSource = source;
19382 err = kNoErr;
19383
19384 exit:
19385 return( err );
19386 }
19387
19388 //===========================================================================================================================
19389 // DispatchTimerCreate
19390 //===========================================================================================================================
19391
19392 static OSStatus
19393 DispatchTimerCreate(
19394 dispatch_time_t inStart,
19395 uint64_t inIntervalNs,
19396 uint64_t inLeewayNs,
19397 dispatch_queue_t inQueue,
19398 DispatchHandler inEventHandler,
19399 DispatchHandler inCancelHandler,
19400 void * inContext,
19401 dispatch_source_t * outTimer )
19402 {
19403 OSStatus err;
19404 dispatch_source_t timer;
19405
19406 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
19407 require_action( timer, exit, err = kUnknownErr );
19408
19409 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
19410 dispatch_set_context( timer, inContext );
19411 dispatch_source_set_event_handler_f( timer, inEventHandler );
19412 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
19413
19414 *outTimer = timer;
19415 err = kNoErr;
19416
19417 exit:
19418 return( err );
19419 }
19420
19421 //===========================================================================================================================
19422 // DispatchProcessMonitorCreate
19423 //===========================================================================================================================
19424
19425 static OSStatus
19426 DispatchProcessMonitorCreate(
19427 pid_t inPID,
19428 unsigned long inFlags,
19429 dispatch_queue_t inQueue,
19430 DispatchHandler inEventHandler,
19431 DispatchHandler inCancelHandler,
19432 void * inContext,
19433 dispatch_source_t * outMonitor )
19434 {
19435 OSStatus err;
19436 dispatch_source_t monitor;
19437
19438 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
19439 inQueue ? inQueue : dispatch_get_main_queue() );
19440 require_action( monitor, exit, err = kUnknownErr );
19441
19442 dispatch_set_context( monitor, inContext );
19443 dispatch_source_set_event_handler_f( monitor, inEventHandler );
19444 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
19445
19446 *outMonitor = monitor;
19447 err = kNoErr;
19448
19449 exit:
19450 return( err );
19451 }
19452
19453 //===========================================================================================================================
19454 // ServiceTypeDescription
19455 //===========================================================================================================================
19456
19457 typedef struct
19458 {
19459 const char * name; // Name of the service type in two-label "_service._proto" format.
19460 const char * description; // Description of the service type.
19461
19462 } ServiceType;
19463
19464 // A Non-comprehensive table of DNS-SD service types
19465
19466 static const ServiceType kServiceTypes[] =
19467 {
19468 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19469 { "_adisk._tcp", "Automatic Disk Discovery" },
19470 { "_afpovertcp._tcp", "Apple File Sharing" },
19471 { "_airdrop._tcp", "AirDrop" },
19472 { "_airplay._tcp", "AirPlay" },
19473 { "_airport._tcp", "AirPort Base Station" },
19474 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19475 { "_eppc._tcp", "Remote AppleEvents" },
19476 { "_ftp._tcp", "File Transfer Protocol" },
19477 { "_home-sharing._tcp", "Home Sharing" },
19478 { "_homekit._tcp", "HomeKit" },
19479 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19480 { "_https._tcp", "HTTP over SSL/TLS" },
19481 { "_ipp._tcp", "Internet Printing Protocol" },
19482 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19483 { "_mediaremotetv._tcp", "Media Remote" },
19484 { "_net-assistant._tcp", "Apple Remote Desktop" },
19485 { "_od-master._tcp", "OpenDirectory Master" },
19486 { "_nfs._tcp", "Network File System" },
19487 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19488 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19489 { "_raop._tcp", "Remote Audio Output Protocol" },
19490 { "_rfb._tcp", "Remote Frame Buffer" },
19491 { "_scanner._tcp", "Bonjour Scanning" },
19492 { "_smb._tcp", "Server Message Block over TCP/IP" },
19493 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19494 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19495 { "_ssh._tcp", "SSH Remote Login Protocol" },
19496 { "_teleport._tcp", "teleport" },
19497 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19498 { "_workstation._tcp", "Workgroup Manager" },
19499 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19500 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19501 };
19502
19503 static const char * ServiceTypeDescription( const char *inName )
19504 {
19505 const ServiceType * serviceType;
19506 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
19507
19508 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
19509 {
19510 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
19511 {
19512 const char * const ptr = &inName[ strlen( serviceType->name ) ];
19513
19514 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
19515 {
19516 return( serviceType->description );
19517 }
19518 }
19519 }
19520 return( NULL );
19521 }
19522
19523 //===========================================================================================================================
19524 // SocketContextCreate
19525 //===========================================================================================================================
19526
19527 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
19528 {
19529 OSStatus err;
19530 SocketContext * context;
19531
19532 context = (SocketContext *) calloc( 1, sizeof( *context ) );
19533 require_action( context, exit, err = kNoMemoryErr );
19534
19535 context->refCount = 1;
19536 context->sock = inSock;
19537 context->userContext = inUserContext;
19538
19539 *outContext = context;
19540 err = kNoErr;
19541
19542 exit:
19543 return( err );
19544 }
19545
19546 //===========================================================================================================================
19547 // SocketContextRetain
19548 //===========================================================================================================================
19549
19550 static SocketContext * SocketContextRetain( SocketContext *inContext )
19551 {
19552 ++inContext->refCount;
19553 return( inContext );
19554 }
19555
19556 //===========================================================================================================================
19557 // SocketContextRelease
19558 //===========================================================================================================================
19559
19560 static void SocketContextRelease( SocketContext *inContext )
19561 {
19562 if( --inContext->refCount == 0 )
19563 {
19564 ForgetSocket( &inContext->sock );
19565 free( inContext );
19566 }
19567 }
19568
19569 //===========================================================================================================================
19570 // SocketContextCancelHandler
19571 //===========================================================================================================================
19572
19573 static void SocketContextCancelHandler( void *inContext )
19574 {
19575 SocketContextRelease( (SocketContext *) inContext );
19576 }
19577
19578 //===========================================================================================================================
19579 // StringToInt32
19580 //===========================================================================================================================
19581
19582 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
19583 {
19584 OSStatus err;
19585 long value;
19586 char * endPtr;
19587
19588 value = strtol( inString, &endPtr, 0 );
19589 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19590 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
19591
19592 *outValue = (int32_t) value;
19593 err = kNoErr;
19594
19595 exit:
19596 return( err );
19597 }
19598
19599 //===========================================================================================================================
19600 // StringToUInt32
19601 //===========================================================================================================================
19602
19603 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
19604 {
19605 OSStatus err;
19606 uint32_t value;
19607 char * endPtr;
19608
19609 value = (uint32_t) strtol( inString, &endPtr, 0 );
19610 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
19611
19612 *outValue = value;
19613 err = kNoErr;
19614
19615 exit:
19616 return( err );
19617 }
19618
19619 #if( TARGET_OS_DARWIN )
19620 //===========================================================================================================================
19621 // _StringToInt64
19622 //===========================================================================================================================
19623
19624 static int64_t _StringToInt64( const char *inString, OSStatus *outError )
19625 {
19626 OSStatus err;
19627 long long val;
19628 char * end;
19629 int errnoVal;
19630
19631 set_errno_compat( 0 );
19632 val = strtoll( inString, &end, 0 );
19633 errnoVal = errno_compat();
19634
19635 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19636 require_action_quiet( ( ( val != LLONG_MIN ) && ( val != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19637 require_action_quiet( ( val >= INT64_MIN ) && ( val <= INT64_MAX ), exit, err = kRangeErr );
19638 err = kNoErr;
19639
19640 exit:
19641 if( outError ) *outError = err;
19642 return( (int64_t)val );
19643 }
19644
19645 //===========================================================================================================================
19646 // _StringToUInt64
19647 //===========================================================================================================================
19648
19649 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError )
19650 {
19651 OSStatus err;
19652 unsigned long long val;
19653 char * end;
19654 int errnoVal;
19655
19656 set_errno_compat( 0 );
19657 val = strtoull( inString, &end, 0 );
19658 errnoVal = errno_compat();
19659
19660 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
19661 require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
19662 require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
19663 err = kNoErr;
19664
19665 exit:
19666 if( outError ) *outError = err;
19667 return( (uint64_t)val );
19668 }
19669
19670 //===========================================================================================================================
19671 // _StringToPID
19672 //===========================================================================================================================
19673
19674 static pid_t _StringToPID( const char *inString, OSStatus *outError )
19675 {
19676 OSStatus err;
19677 int64_t val;
19678
19679 val = _StringToInt64( inString, &err );
19680 require_noerr_quiet( err, exit );
19681 require_action_quiet( val == (pid_t) val, exit, err = kRangeErr );
19682 err = kNoErr;
19683
19684 exit:
19685 if( outError ) *outError = err;
19686 return( (pid_t) val );
19687 }
19688
19689 //===========================================================================================================================
19690 // _ParseEscapedString
19691 //
19692 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19693 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19694 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19695 //===========================================================================================================================
19696
19697 static OSStatus
19698 _ParseEscapedString(
19699 const char * inSrc,
19700 const char * inEnd,
19701 const char * inDelimiters,
19702 char * inBufPtr,
19703 size_t inBufLen,
19704 size_t * outCopiedLen,
19705 size_t * outActualLen,
19706 const char ** outPtr )
19707 {
19708 OSStatus err;
19709 const char * ptr;
19710 char * dst = inBufPtr;
19711 const char * const lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
19712 size_t len;
19713
19714 len = 0;
19715 ptr = inSrc;
19716 if( !inDelimiters ) inDelimiters = "";
19717 while( ptr < inEnd )
19718 {
19719 int c;
19720 const char * del;
19721
19722 c = *ptr;
19723 for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
19724 if( *del != '\0' ) break;
19725 ++ptr;
19726 if( c == '\\' )
19727 {
19728 require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
19729 c = *ptr++;
19730 }
19731 ++len;
19732 if( dst < lim ) *dst++ = (char) c;
19733 }
19734 if( inBufLen > 0 ) *dst = '\0';
19735
19736 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBufPtr );
19737 if( outActualLen ) *outActualLen = len;
19738 if( outPtr ) *outPtr = ptr;
19739 err = kNoErr;
19740
19741 exit:
19742 return( err );
19743 }
19744 #endif
19745
19746 //===========================================================================================================================
19747 // StringToARecordData
19748 //===========================================================================================================================
19749
19750 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19751 {
19752 OSStatus err;
19753 uint32_t * addrPtr;
19754 const size_t addrLen = sizeof( *addrPtr );
19755 const char * end;
19756
19757 addrPtr = (uint32_t *) malloc( addrLen );
19758 require_action( addrPtr, exit, err = kNoMemoryErr );
19759
19760 err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
19761 NULL, NULL, NULL, &end );
19762 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19763 require_noerr_quiet( err, exit );
19764
19765 *addrPtr = HostToBig32( *addrPtr );
19766
19767 *outPtr = (uint8_t *) addrPtr;
19768 addrPtr = NULL;
19769 *outLen = addrLen;
19770
19771 exit:
19772 FreeNullSafe( addrPtr );
19773 return( err );
19774 }
19775
19776 //===========================================================================================================================
19777 // StringToAAAARecordData
19778 //===========================================================================================================================
19779
19780 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
19781 {
19782 OSStatus err;
19783 uint8_t * addrPtr;
19784 const size_t addrLen = 16;
19785 const char * end;
19786
19787 addrPtr = (uint8_t *) malloc( addrLen );
19788 require_action( addrPtr, exit, err = kNoMemoryErr );
19789
19790 err = _StringToIPv6Address( inString,
19791 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
19792 addrPtr, NULL, NULL, NULL, &end );
19793 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19794 require_noerr_quiet( err, exit );
19795
19796 *outPtr = addrPtr;
19797 addrPtr = NULL;
19798 *outLen = addrLen;
19799
19800 exit:
19801 FreeNullSafe( addrPtr );
19802 return( err );
19803 }
19804
19805 //===========================================================================================================================
19806 // StringToDomainName
19807 //===========================================================================================================================
19808
19809 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
19810 {
19811 OSStatus err;
19812 uint8_t * namePtr;
19813 size_t nameLen;
19814 uint8_t * end;
19815 uint8_t nameBuf[ kDomainNameLengthMax ];
19816
19817 err = DomainNameFromString( nameBuf, inString, &end );
19818 require_noerr_quiet( err, exit );
19819
19820 nameLen = (size_t)( end - nameBuf );
19821 namePtr = _memdup( nameBuf, nameLen );
19822 require_action( namePtr, exit, err = kNoMemoryErr );
19823
19824 *outPtr = namePtr;
19825 namePtr = NULL;
19826 if( outLen ) *outLen = nameLen;
19827
19828 exit:
19829 return( err );
19830 }
19831
19832 #if( TARGET_OS_DARWIN )
19833 //===========================================================================================================================
19834 // GetDefaultDNSServer
19835 //===========================================================================================================================
19836
19837 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
19838 {
19839 OSStatus err;
19840 dns_config_t * config;
19841 struct sockaddr * addr;
19842 int32_t i;
19843
19844 config = dns_configuration_copy();
19845 require_action( config, exit, err = kUnknownErr );
19846
19847 addr = NULL;
19848 for( i = 0; i < config->n_resolver; ++i )
19849 {
19850 const dns_resolver_t * const resolver = config->resolver[ i ];
19851
19852 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
19853 {
19854 addr = resolver->nameserver[ 0 ];
19855 break;
19856 }
19857 }
19858 require_action_quiet( addr, exit, err = kNotFoundErr );
19859
19860 SockAddrCopy( addr, outAddr );
19861 err = kNoErr;
19862
19863 exit:
19864 if( config ) dns_configuration_free( config );
19865 return( err );
19866 }
19867 #endif
19868
19869 //===========================================================================================================================
19870 // GetMDNSMulticastAddrV4
19871 //===========================================================================================================================
19872
19873 static void _MDNSMulticastAddrV4Init( void *inContext );
19874
19875 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
19876 {
19877 static struct sockaddr_in sMDNSMulticastAddrV4;
19878 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
19879
19880 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
19881 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
19882 }
19883
19884 static void _MDNSMulticastAddrV4Init( void *inContext )
19885 {
19886 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
19887
19888 memset( addr, 0, sizeof( *addr ) );
19889 SIN_LEN_SET( addr );
19890 addr->sin_family = AF_INET;
19891 addr->sin_port = htons( kMDNSPort );
19892 addr->sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19893 }
19894
19895 //===========================================================================================================================
19896 // GetMDNSMulticastAddrV6
19897 //===========================================================================================================================
19898
19899 static void _MDNSMulticastAddrV6Init( void *inContext );
19900
19901 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
19902 {
19903 static struct sockaddr_in6 sMDNSMulticastAddrV6;
19904 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
19905
19906 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
19907 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
19908 }
19909
19910 static void _MDNSMulticastAddrV6Init( void *inContext )
19911 {
19912 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
19913
19914 memset( addr, 0, sizeof( *addr ) );
19915 SIN6_LEN_SET( addr );
19916 addr->sin6_family = AF_INET6;
19917 addr->sin6_port = htons( kMDNSPort );
19918 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19919 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
19920 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
19921 }
19922
19923 //===========================================================================================================================
19924 // CreateMulticastSocket
19925 //===========================================================================================================================
19926
19927 static OSStatus
19928 CreateMulticastSocket(
19929 const struct sockaddr * inAddr,
19930 int inPort,
19931 const char * inIfName,
19932 uint32_t inIfIndex,
19933 Boolean inJoin,
19934 int * outPort,
19935 SocketRef * outSock )
19936 {
19937 OSStatus err;
19938 SocketRef sock = kInvalidSocketRef;
19939 const int family = inAddr->sa_family;
19940 int port;
19941
19942 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
19943
19944 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
19945 require_noerr_quiet( err, exit );
19946
19947 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
19948 require_noerr_quiet( err, exit );
19949
19950 if( family == AF_INET )
19951 {
19952 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
19953 err = map_socket_noerr_errno( sock, err );
19954 require_noerr_quiet( err, exit );
19955 }
19956 else
19957 {
19958 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
19959 err = map_socket_noerr_errno( sock, err );
19960 require_noerr_quiet( err, exit );
19961 }
19962
19963 if( inJoin )
19964 {
19965 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
19966 require_noerr_quiet( err, exit );
19967 }
19968
19969 if( outPort ) *outPort = port;
19970 *outSock = sock;
19971 sock = kInvalidSocketRef;
19972
19973 exit:
19974 ForgetSocket( &sock );
19975 return( err );
19976 }
19977
19978 //===========================================================================================================================
19979 // DecimalTextToUInt32
19980 //===========================================================================================================================
19981
19982 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
19983 {
19984 OSStatus err;
19985 uint64_t value;
19986 const char * ptr = inSrc;
19987
19988 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
19989
19990 value = (uint64_t)( *ptr++ - '0' );
19991 if( value == 0 )
19992 {
19993 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
19994 {
19995 err = kMalformedErr;
19996 goto exit;
19997 }
19998 }
19999 else
20000 {
20001 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
20002 {
20003 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
20004 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
20005 }
20006 }
20007
20008 *outValue = (uint32_t) value;
20009 if( outPtr ) *outPtr = ptr;
20010 err = kNoErr;
20011
20012 exit:
20013 return( err );
20014 }
20015
20016 //===========================================================================================================================
20017 // CheckIntegerArgument
20018 //===========================================================================================================================
20019
20020 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
20021 {
20022 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
20023
20024 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
20025 return( kRangeErr );
20026 }
20027
20028 //===========================================================================================================================
20029 // CheckDoubleArgument
20030 //===========================================================================================================================
20031
20032 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
20033 {
20034 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
20035
20036 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
20037 return( kRangeErr );
20038 }
20039
20040 //===========================================================================================================================
20041 // CheckRootUser
20042 //===========================================================================================================================
20043
20044 static OSStatus CheckRootUser( void )
20045 {
20046 if( geteuid() == 0 ) return( kNoErr );
20047
20048 FPrintF( stderr, "error: This command must to be run as root.\n" );
20049 return( kPermissionErr );
20050 }
20051
20052 //===========================================================================================================================
20053 // SpawnCommand
20054 //
20055 // Note: Based on systemf() from CoreUtils framework.
20056 //===========================================================================================================================
20057
20058 extern char ** environ;
20059
20060 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... )
20061 {
20062 OSStatus err;
20063 va_list args;
20064 char * command;
20065 char * argv[ 4 ];
20066 pid_t pid;
20067
20068 command = NULL;
20069 va_start( args, inFormat );
20070 VASPrintF( &command, inFormat, args );
20071 va_end( args );
20072 require_action( command, exit, err = kUnknownErr );
20073
20074 argv[ 0 ] = "/bin/sh";
20075 argv[ 1 ] = "-c";
20076 argv[ 2 ] = command;
20077 argv[ 3 ] = NULL;
20078 err = posix_spawn( &pid, argv[ 0 ], NULL, NULL, argv, environ );
20079 free( command );
20080 require_noerr_quiet( err, exit );
20081
20082 if( outPID ) *outPID = pid;
20083
20084 exit:
20085 return( err );
20086 }
20087
20088 //===========================================================================================================================
20089 // OutputFormatFromArgString
20090 //===========================================================================================================================
20091
20092 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
20093 {
20094 OSStatus err;
20095 OutputFormatType format;
20096
20097 format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
20098 kOutputFormatStr_JSON, kOutputFormatType_JSON,
20099 kOutputFormatStr_XML, kOutputFormatType_XML,
20100 kOutputFormatStr_Binary, kOutputFormatType_Binary,
20101 NULL );
20102 if( outFormat ) *outFormat = format;
20103 return( err );
20104 }
20105
20106 //===========================================================================================================================
20107 // OutputPropertyList
20108 //===========================================================================================================================
20109
20110 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
20111 {
20112 OSStatus err;
20113 CFDataRef results = NULL;
20114 FILE * file = NULL;
20115
20116 // Convert plist to a specific format.
20117
20118 switch( inType )
20119 {
20120 case kOutputFormatType_JSON:
20121 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
20122 require_action( results, exit, err = kUnknownErr );
20123 break;
20124
20125 case kOutputFormatType_XML:
20126 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
20127 require_action( results, exit, err = kUnknownErr );
20128 break;
20129
20130 case kOutputFormatType_Binary:
20131 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
20132 require_action( results, exit, err = kUnknownErr );
20133 break;
20134
20135 default:
20136 err = kTypeErr;
20137 goto exit;
20138 }
20139
20140 // Write formatted results to file or stdout.
20141
20142 if( inOutputFilePath )
20143 {
20144 file = fopen( inOutputFilePath, "wb" );
20145 err = map_global_value_errno( file, file );
20146 require_noerr( err, exit );
20147 }
20148 else
20149 {
20150 file = stdout;
20151 }
20152
20153 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
20154 require_noerr_quiet( err, exit );
20155
20156 // Write a trailing newline for JSON-formatted results.
20157
20158 if( inType == kOutputFormatType_JSON )
20159 {
20160 err = WriteANSIFile( file, "\n", 1 );
20161 require_noerr_quiet( err, exit );
20162 }
20163
20164 exit:
20165 if( file && ( file != stdout ) ) fclose( file );
20166 CFReleaseNullSafe( results );
20167 return( err );
20168 }
20169
20170 //===========================================================================================================================
20171 // CreateSRVRecordDataFromString
20172 //===========================================================================================================================
20173
20174 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
20175 {
20176 OSStatus err;
20177 DataBuffer dataBuf;
20178 const char * ptr;
20179 int i;
20180 uint8_t * end;
20181 uint8_t target[ kDomainNameLengthMax ];
20182
20183 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
20184
20185 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20186
20187 ptr = inString;
20188 for( i = 0; i < 3; ++i )
20189 {
20190 char * next;
20191 long value;
20192 uint8_t buf[ 2 ];
20193
20194 value = strtol( ptr, &next, 0 );
20195 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
20196 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
20197 ptr = next + 1;
20198
20199 WriteBig16( buf, value );
20200
20201 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
20202 require_noerr( err, exit );
20203 }
20204
20205 // Set the target domain name.
20206
20207 err = DomainNameFromString( target, ptr, &end );
20208 require_noerr_quiet( err, exit );
20209
20210 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
20211 require_noerr( err, exit );
20212
20213 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20214 require_noerr( err, exit );
20215
20216 exit:
20217 DataBuffer_Free( &dataBuf );
20218 return( err );
20219 }
20220
20221 //===========================================================================================================================
20222 // CreateTXTRecordDataFromString
20223 //===========================================================================================================================
20224
20225 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
20226 {
20227 OSStatus err;
20228 DataBuffer dataBuf;
20229 const char * src;
20230 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20231
20232 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
20233
20234 src = inString;
20235 for( ;; )
20236 {
20237 uint8_t * dst = &txtStr[ 1 ];
20238 const uint8_t * const lim = &txtStr[ 256 ];
20239 int c;
20240
20241 while( *src && ( *src != inDelimiter ) )
20242 {
20243 if( ( c = *src++ ) == '\\' )
20244 {
20245 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
20246 c = *src++;
20247 }
20248 require_action_quiet( dst < lim, exit, err = kOverrunErr );
20249 *dst++ = (uint8_t) c;
20250 }
20251 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
20252 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
20253 require_noerr( err, exit );
20254
20255 if( *src == '\0' ) break;
20256 ++src;
20257 }
20258
20259 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
20260 require_noerr( err, exit );
20261
20262 exit:
20263 DataBuffer_Free( &dataBuf );
20264 return( err );
20265 }
20266
20267 //===========================================================================================================================
20268 // CreateNSECRecordData
20269 //===========================================================================================================================
20270
20271 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
20272 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
20273
20274 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20275
20276 static OSStatus
20277 CreateNSECRecordData(
20278 const uint8_t * inNextDomainName,
20279 uint8_t ** outPtr,
20280 size_t * outLen,
20281 unsigned int inTypeCount,
20282 ... )
20283 {
20284 OSStatus err;
20285 va_list args;
20286 DataBuffer rdataDB;
20287 unsigned int * array = NULL;
20288 unsigned int i, type, maxBit, currBlock, bitmapLen;
20289 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
20290 uint8_t * const bitmap = &fields[ 2 ];
20291
20292 va_start( args, inTypeCount );
20293 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20294
20295 // Append Next Domain Name.
20296
20297 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
20298 require_noerr( err, exit );
20299
20300 // Append Type Bit Maps.
20301
20302 maxBit = 0;
20303 memset( bitmap, 0, kNSECBitmapMaxLength );
20304 if( inTypeCount > 0 )
20305 {
20306 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
20307 require_action( array, exit, err = kNoMemoryErr );
20308
20309 for( i = 0; i < inTypeCount; ++i )
20310 {
20311 type = va_arg( args, unsigned int );
20312 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
20313 array[ i ] = type;
20314 }
20315 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
20316
20317 currBlock = array[ 0 ] / 256;
20318 for( i = 0; i < inTypeCount; ++i )
20319 {
20320 const unsigned int block = array[ i ] / 256;
20321 const unsigned int bit = array[ i ] % 256;
20322
20323 if( block != currBlock )
20324 {
20325 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20326 fields[ 0 ] = (uint8_t) currBlock;
20327 fields[ 1 ] = (uint8_t) bitmapLen;
20328
20329 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20330 require_noerr( err, exit );
20331
20332 maxBit = 0;
20333 currBlock = block;
20334 memset( bitmap, 0, bitmapLen );
20335 }
20336 BitArray_SetBit( bitmap, bit );
20337 if( bit > maxBit ) maxBit = bit;
20338 }
20339 }
20340 else
20341 {
20342 currBlock = 0;
20343 }
20344
20345 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
20346 fields[ 0 ] = (uint8_t) currBlock;
20347 fields[ 1 ] = (uint8_t) bitmapLen;
20348
20349 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
20350 require_noerr( err, exit );
20351
20352 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20353 require_noerr( err, exit );
20354
20355 exit:
20356 va_end( args );
20357 DataBuffer_Free( &rdataDB );
20358 FreeNullSafe( array );
20359 return( err );
20360 }
20361
20362 //===========================================================================================================================
20363 // AppendSOARecord
20364 //===========================================================================================================================
20365
20366 static OSStatus
20367 _AppendSOARecordData(
20368 DataBuffer * inDB,
20369 const uint8_t * inMName,
20370 const uint8_t * inRName,
20371 uint32_t inSerial,
20372 uint32_t inRefresh,
20373 uint32_t inRetry,
20374 uint32_t inExpire,
20375 uint32_t inMinimumTTL,
20376 size_t * outLen );
20377
20378 static OSStatus
20379 AppendSOARecord(
20380 DataBuffer * inDB,
20381 const uint8_t * inNamePtr,
20382 size_t inNameLen,
20383 uint16_t inType,
20384 uint16_t inClass,
20385 uint32_t inTTL,
20386 const uint8_t * inMName,
20387 const uint8_t * inRName,
20388 uint32_t inSerial,
20389 uint32_t inRefresh,
20390 uint32_t inRetry,
20391 uint32_t inExpire,
20392 uint32_t inMinimumTTL,
20393 size_t * outLen )
20394 {
20395 OSStatus err;
20396 size_t rdataLen;
20397 size_t rdlengthOffset = 0;
20398 uint8_t * rdlengthPtr;
20399
20400 if( inDB )
20401 {
20402 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
20403 require_noerr( err, exit );
20404
20405 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
20406 }
20407
20408 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
20409 require_noerr( err, exit );
20410
20411 if( inDB )
20412 {
20413 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
20414 WriteBig16( rdlengthPtr, rdataLen );
20415 }
20416
20417 if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
20418 err = kNoErr;
20419
20420 exit:
20421 return( err );
20422 }
20423
20424 static OSStatus
20425 _AppendSOARecordData(
20426 DataBuffer * inDB,
20427 const uint8_t * inMName,
20428 const uint8_t * inRName,
20429 uint32_t inSerial,
20430 uint32_t inRefresh,
20431 uint32_t inRetry,
20432 uint32_t inExpire,
20433 uint32_t inMinimumTTL,
20434 size_t * outLen )
20435 {
20436 OSStatus err;
20437 dns_fixed_fields_soa fields;
20438 const size_t mnameLen = DomainNameLength( inMName );
20439 const size_t rnameLen = DomainNameLength( inRName );
20440
20441 if( inDB )
20442 {
20443 err = DataBuffer_Append( inDB, inMName, mnameLen );
20444 require_noerr( err, exit );
20445
20446 err = DataBuffer_Append( inDB, inRName, rnameLen );
20447 require_noerr( err, exit );
20448
20449 dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
20450 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20451 require_noerr( err, exit );
20452 }
20453 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
20454 err = kNoErr;
20455
20456 exit:
20457 return( err );
20458 }
20459
20460 //===========================================================================================================================
20461 // CreateSOARecordData
20462 //===========================================================================================================================
20463
20464 static OSStatus
20465 CreateSOARecordData(
20466 const uint8_t * inMName,
20467 const uint8_t * inRName,
20468 uint32_t inSerial,
20469 uint32_t inRefresh,
20470 uint32_t inRetry,
20471 uint32_t inExpire,
20472 uint32_t inMinimumTTL,
20473 uint8_t ** outPtr,
20474 size_t * outLen )
20475 {
20476 OSStatus err;
20477 DataBuffer rdataDB;
20478
20479 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
20480
20481 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
20482 require_noerr( err, exit );
20483
20484 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
20485 require_noerr( err, exit );
20486
20487 exit:
20488 DataBuffer_Free( &rdataDB );
20489 return( err );
20490 }
20491
20492 //===========================================================================================================================
20493 // _DataBuffer_AppendDNSQuestion
20494 //===========================================================================================================================
20495
20496 static OSStatus
20497 _DataBuffer_AppendDNSQuestion(
20498 DataBuffer * inDB,
20499 const uint8_t * inNamePtr,
20500 size_t inNameLen,
20501 uint16_t inType,
20502 uint16_t inClass )
20503 {
20504 OSStatus err;
20505 dns_fixed_fields_question fields;
20506
20507 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20508 require_noerr( err, exit );
20509
20510 dns_fixed_fields_question_init( &fields, inType, inClass );
20511 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20512 require_noerr( err, exit );
20513
20514 exit:
20515 return( err );
20516 }
20517
20518 //===========================================================================================================================
20519 // _DataBuffer_AppendDNSRecord
20520 //===========================================================================================================================
20521
20522 static OSStatus
20523 _DataBuffer_AppendDNSRecord(
20524 DataBuffer * inDB,
20525 const uint8_t * inNamePtr,
20526 size_t inNameLen,
20527 uint16_t inType,
20528 uint16_t inClass,
20529 uint32_t inTTL,
20530 const uint8_t * inRDataPtr,
20531 size_t inRDataLen )
20532 {
20533 OSStatus err;
20534 dns_fixed_fields_record fields;
20535
20536 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
20537
20538 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
20539 require_noerr( err, exit );
20540
20541 dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
20542 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
20543 require_noerr( err, exit );
20544
20545 if( inRDataPtr )
20546 {
20547 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
20548 require_noerr( err, exit );
20549 }
20550
20551 exit:
20552 return( err );
20553 }
20554
20555 //===========================================================================================================================
20556 // _NanoTime64ToTimestamp
20557 //===========================================================================================================================
20558
20559 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
20560 {
20561 struct timeval tv;
20562
20563 NanoTimeToTimeVal( inTime, &tv );
20564 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
20565 }
20566
20567 //===========================================================================================================================
20568 // _MDNSInterfaceListCreate
20569 //===========================================================================================================================
20570
20571 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
20572
20573 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
20574 {
20575 OSStatus err;
20576 struct ifaddrs * ifaList;
20577 const struct ifaddrs * ifa;
20578 MDNSInterfaceItem * interfaceList;
20579 MDNSInterfaceItem ** ptr;
20580 SocketRef infoSock;
20581
20582 ifaList = NULL;
20583 interfaceList = NULL;
20584 infoSock = kInvalidSocketRef;
20585 if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
20586 require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
20587
20588 infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
20589 err = map_socket_creation_errno( infoSock );
20590 require_noerr( err, exit );
20591
20592 err = getifaddrs( &ifaList );
20593 err = map_global_noerr_errno( err );
20594 require_noerr( err, exit );
20595
20596 ptr = &interfaceList;
20597 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
20598 {
20599 MDNSInterfaceItem * item;
20600 int family;
20601 const unsigned int flagsMask = IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
20602 const unsigned int flagsNeeded = IFF_UP | IFF_MULTICAST;
20603
20604 if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded ) continue;
20605 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
20606 family = ifa->ifa_addr->sa_family;
20607 if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
20608
20609 for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
20610 if( !item )
20611 {
20612 NetTransportType type;
20613 uint32_t ifIndex;
20614 const char * const ifName = ifa->ifa_name;
20615
20616 if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
20617 err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
20618 require_noerr( err, exit );
20619
20620 if( ifIndex == 0 ) continue;
20621 if( type == kNetTransportType_AWDL )
20622 {
20623 if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
20624 }
20625 else
20626 {
20627 if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
20628 }
20629 item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
20630 require_action( item, exit, err = kNoMemoryErr );
20631
20632 *ptr = item;
20633 ptr = &item->next;
20634
20635 item->ifName = strdup( ifName );
20636 require_action( item->ifName, exit, err = kNoMemoryErr );
20637
20638 item->ifIndex = ifIndex;
20639 if( type == kNetTransportType_AWDL ) item->isAWDL = true;
20640 else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
20641 }
20642 if( family == AF_INET ) item->hasIPv4 = true;
20643 else item->hasIPv6 = true;
20644 }
20645 require_action_quiet( interfaceList, exit, err = kNotFoundErr );
20646
20647 if( outList )
20648 {
20649 *outList = interfaceList;
20650 interfaceList = NULL;
20651 }
20652
20653 exit:
20654 if( ifaList ) freeifaddrs( ifaList );
20655 _MDNSInterfaceListFree( interfaceList );
20656 ForgetSocket( &infoSock );
20657 return( err );
20658 }
20659
20660 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
20661 {
20662 OSStatus err;
20663 int i;
20664 static const char * const kMDNSInterfacePrefixBlacklist[] = { "llw" };
20665 struct ifreq ifr;
20666
20667 // Check if the interface name's prefix matches the prefix blacklist.
20668
20669 for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
20670 {
20671 const char * const prefix = kMDNSInterfacePrefixBlacklist[ i ];
20672
20673 if( strcmp_prefix( inIfName, prefix ) == 0 )
20674 {
20675 const char * ptr = &inIfName[ strlen( prefix ) ];
20676
20677 while( isdigit_safe( *ptr ) ) ++ptr;
20678 if( *ptr == '\0' ) return( true );
20679 }
20680 }
20681
20682 // Check if the interface is used for inter-(co)processor networking.
20683
20684 memset( &ifr, 0, sizeof( ifr ) );
20685 strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
20686 err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
20687 err = map_global_value_errno( err != -1, err );
20688 if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
20689
20690 return( false );
20691 }
20692
20693 //===========================================================================================================================
20694 // _MDNSInterfaceListFree
20695 //===========================================================================================================================
20696
20697 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList )
20698 {
20699 MDNSInterfaceItem * item;
20700
20701 while( ( item = inList ) != NULL )
20702 {
20703 inList = item->next;
20704 FreeNullSafe( item->ifName );
20705 free( item );
20706 }
20707 }
20708
20709 //===========================================================================================================================
20710 // _MDNSInterfaceGetAny
20711 //===========================================================================================================================
20712
20713 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
20714 {
20715 OSStatus err;
20716 MDNSInterfaceItem * list;
20717 const MDNSInterfaceItem * item;
20718
20719 list = NULL;
20720 err = _MDNSInterfaceListCreate( inSubset, 0, &list );
20721 require_noerr_quiet( err, exit );
20722 require_action_quiet( list, exit, err = kNotFoundErr );
20723
20724 for( item = list; item; item = item->next )
20725 {
20726 if( item->hasIPv4 && item->hasIPv6 ) break;
20727 }
20728 if( !item ) item = list;
20729 if( inNameBuf ) strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
20730 if( outIndex ) *outIndex = item->ifIndex;
20731
20732 exit:
20733 _MDNSInterfaceListFree( list );
20734 return( err );
20735 }
20736
20737 //===========================================================================================================================
20738 // _SetComputerName
20739 //===========================================================================================================================
20740
20741 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
20742 {
20743 OSStatus err;
20744 SCPreferencesRef prefs;
20745 Boolean ok;
20746
20747 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20748 err = map_scerror( prefs );
20749 require_noerr_quiet( err, exit );
20750
20751 ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
20752 err = map_scerror( ok );
20753 require_noerr_quiet( err, exit );
20754
20755 ok = SCPreferencesCommitChanges( prefs );
20756 err = map_scerror( ok );
20757 require_noerr_quiet( err, exit );
20758
20759 ok = SCPreferencesApplyChanges( prefs );
20760 err = map_scerror( ok );
20761 require_noerr_quiet( err, exit );
20762
20763 exit:
20764 CFReleaseNullSafe( prefs );
20765 return( err );
20766 }
20767
20768 //===========================================================================================================================
20769 // _SetComputerNameWithUTF8CString
20770 //===========================================================================================================================
20771
20772 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName )
20773 {
20774 OSStatus err;
20775 CFStringRef computerName;
20776
20777 computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
20778 require_action( computerName, exit, err = kNoMemoryErr );
20779
20780 err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
20781 require_noerr_quiet( err, exit );
20782
20783 exit:
20784 CFReleaseNullSafe( computerName );
20785 return( err );
20786 }
20787
20788 //===========================================================================================================================
20789 // _SetLocalHostName
20790 //===========================================================================================================================
20791
20792 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName )
20793 {
20794 OSStatus err;
20795 SCPreferencesRef prefs;
20796 Boolean ok;
20797
20798 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
20799 err = map_scerror( prefs );
20800 require_noerr_quiet( err, exit );
20801
20802 ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
20803 err = map_scerror( ok );
20804 require_noerr_quiet( err, exit );
20805
20806 ok = SCPreferencesCommitChanges( prefs );
20807 err = map_scerror( ok );
20808 require_noerr_quiet( err, exit );
20809
20810 ok = SCPreferencesApplyChanges( prefs );
20811 err = map_scerror( ok );
20812 require_noerr_quiet( err, exit );
20813
20814 exit:
20815 CFReleaseNullSafe( prefs );
20816 return( err );
20817 }
20818
20819 //===========================================================================================================================
20820 // _SetLocalHostNameWithUTF8CString
20821 //===========================================================================================================================
20822
20823 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
20824 {
20825 OSStatus err;
20826 CFStringRef localHostName;
20827
20828 localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
20829 require_action( localHostName, exit, err = kNoMemoryErr );
20830
20831 err = _SetLocalHostName( localHostName );
20832 require_noerr_quiet( err, exit );
20833
20834 exit:
20835 CFReleaseNullSafe( localHostName );
20836 return( err );
20837 }
20838
20839 //===========================================================================================================================
20840 // MDNSColliderCreate
20841 //===========================================================================================================================
20842
20843 typedef enum
20844 {
20845 kMDNSColliderOpCode_Invalid = 0,
20846 kMDNSColliderOpCode_Send = 1,
20847 kMDNSColliderOpCode_Wait = 2,
20848 kMDNSColliderOpCode_SetProbeActions = 3,
20849 kMDNSColliderOpCode_LoopPush = 4,
20850 kMDNSColliderOpCode_LoopPop = 5,
20851 kMDNSColliderOpCode_Exit = 6
20852
20853 } MDNSColliderOpCode;
20854
20855 typedef struct
20856 {
20857 MDNSColliderOpCode opcode;
20858 uint32_t operand;
20859
20860 } MDNSCInstruction;
20861
20862 #define kMaxLoopDepth 16
20863
20864 struct MDNSColliderPrivate
20865 {
20866 CFRuntimeBase base; // CF object base.
20867 dispatch_queue_t queue; // Queue for collider's events.
20868 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
20869 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
20870 SocketRef sockV4; // IPv4 UDP socket for mDNS.
20871 SocketRef sockV6; // IPv6 UDP socket for mDNS.
20872 uint8_t * target; // Record name being targeted. (malloced)
20873 uint8_t * responsePtr; // Response message pointer. (malloced)
20874 size_t responseLen; // Response message length.
20875 uint8_t * probePtr; // Probe query message pointer. (malloced)
20876 size_t probeLen; // Probe query message length.
20877 unsigned int probeCount; // Count of probe queries received for collider's record.
20878 uint32_t probeActionMap; // Bitmap of actions to take for
20879 MDNSCInstruction * program; // Program to execute.
20880 uint32_t pc; // Program's program counter.
20881 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
20882 uint32_t loopDepth; // Current loop depth.
20883 dispatch_source_t waitTimer; // Timer for program's wait commands.
20884 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
20885 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
20886 void * stopContext; // User's stop handler context.
20887 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
20888 Boolean stopped; // True if the collider has been stopped.
20889 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
20890 };
20891
20892 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
20893 static void _MDNSColliderReadHandler( void *inContext );
20894 static void _MDNSColliderExecuteProgram( void *inContext );
20895 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20896 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
20897
20898 CF_CLASS_DEFINE( MDNSCollider );
20899
20900 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
20901 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20902
20903 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
20904 {
20905 OSStatus err;
20906 MDNSColliderRef obj = NULL;
20907
20908 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
20909
20910 ReplaceDispatchQueue( &obj->queue, inQueue );
20911 obj->sockV4 = kInvalidSocketRef;
20912 obj->sockV6 = kInvalidSocketRef;
20913
20914 *outCollider = obj;
20915 err = kNoErr;
20916
20917 exit:
20918 return( err );
20919 }
20920
20921 //===========================================================================================================================
20922 // _MDNSColliderFinalize
20923 //===========================================================================================================================
20924
20925 static void _MDNSColliderFinalize( CFTypeRef inObj )
20926 {
20927 MDNSColliderRef const me = (MDNSColliderRef) inObj;
20928
20929 check( !me->waitTimer );
20930 check( !me->readSourceV4 );
20931 check( !me->readSourceV6 );
20932 check( !IsValidSocket( me->sockV4 ) );
20933 check( !IsValidSocket( me->sockV6 ) );
20934 ForgetMem( &me->target );
20935 ForgetMem( &me->responsePtr );
20936 ForgetMem( &me->probePtr );
20937 ForgetMem( &me->program );
20938 dispatch_forget( &me->queue );
20939 }
20940
20941 //===========================================================================================================================
20942 // MDNSColliderStart
20943 //===========================================================================================================================
20944
20945 static void _MDNSColliderStart( void *inContext );
20946
20947 static OSStatus MDNSColliderStart( MDNSColliderRef me )
20948 {
20949 OSStatus err;
20950
20951 require_action_quiet( me->target, exit, err = kNotPreparedErr );
20952 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
20953 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
20954 require_action_quiet( me->program, exit, err = kNotPreparedErr );
20955 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
20956 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
20957
20958 CFRetain( me );
20959 dispatch_async_f( me->queue, me, _MDNSColliderStart );
20960 err = kNoErr;
20961
20962 exit:
20963 return( err );
20964 }
20965
20966 static void _MDNSColliderStart( void *inContext )
20967 {
20968 OSStatus err;
20969 MDNSColliderRef const me = (MDNSColliderRef) inContext;
20970 SocketRef sock = kInvalidSocketRef;
20971 SocketContext * sockCtx = NULL;
20972
20973 if( me->protocols & kMDNSColliderProtocol_IPv4 )
20974 {
20975 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20976 require_noerr( err, exit );
20977
20978 err = SocketContextCreate( sock, me, &sockCtx );
20979 require_noerr( err, exit );
20980 sock = kInvalidSocketRef;
20981
20982 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
20983 sockCtx, &me->readSourceV4 );
20984 require_noerr( err, exit );
20985 me->sockV4 = sockCtx->sock;
20986 sockCtx = NULL;
20987
20988 dispatch_resume( me->readSourceV4 );
20989 }
20990
20991 if( me->protocols & kMDNSColliderProtocol_IPv6 )
20992 {
20993 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
20994 require_noerr( err, exit );
20995
20996 err = SocketContextCreate( sock, me, &sockCtx );
20997 require_noerr( err, exit );
20998 sock = kInvalidSocketRef;
20999
21000 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
21001 sockCtx, &me->readSourceV6 );
21002 require_noerr( err, exit );
21003 me->sockV6 = sockCtx->sock;
21004 sockCtx = NULL;
21005
21006 dispatch_resume( me->readSourceV6 );
21007 }
21008
21009 _MDNSColliderExecuteProgram( me );
21010 err = kNoErr;
21011
21012 exit:
21013 ForgetSocket( &sock );
21014 ForgetSocketContext( &sockCtx );
21015 if( err ) _MDNSColliderStop( me, err );
21016 }
21017
21018 //===========================================================================================================================
21019 // MDNSColliderStop
21020 //===========================================================================================================================
21021
21022 static void _MDNSColliderUserStop( void *inContext );
21023
21024 static void MDNSColliderStop( MDNSColliderRef me )
21025 {
21026 CFRetain( me );
21027 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
21028 }
21029
21030 static void _MDNSColliderUserStop( void *inContext )
21031 {
21032 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21033
21034 _MDNSColliderStop( me, kCanceledErr );
21035 CFRelease( me );
21036 }
21037
21038 //===========================================================================================================================
21039 // MDNSColliderSetProtocols
21040 //===========================================================================================================================
21041
21042 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
21043 {
21044 me->protocols = inProtocols;
21045 }
21046
21047 //===========================================================================================================================
21048 // MDNSColliderSetInterfaceIndex
21049 //===========================================================================================================================
21050
21051 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
21052 {
21053 me->interfaceIndex = inInterfaceIndex;
21054 }
21055
21056 //===========================================================================================================================
21057 // MDNSColliderSetProgram
21058 //===========================================================================================================================
21059
21060 #define kMDNSColliderProgCmd_Done "done"
21061 #define kMDNSColliderProgCmd_Loop "loop"
21062 #define kMDNSColliderProgCmd_Send "send"
21063 #define kMDNSColliderProgCmd_Probes "probes"
21064 #define kMDNSColliderProgCmd_Wait "wait"
21065
21066 typedef uint32_t MDNSColliderProbeAction;
21067
21068 #define kMDNSColliderProbeAction_None 0
21069 #define kMDNSColliderProbeAction_Respond 1
21070 #define kMDNSColliderProbeAction_RespondUnicast 2
21071 #define kMDNSColliderProbeAction_RespondMulticast 3
21072 #define kMDNSColliderProbeAction_Probe 4
21073 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21074
21075 #define kMDNSColliderProbeActionBits_Count 3
21076 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21077 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21078
21079 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
21080
21081 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
21082
21083 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
21084 {
21085 OSStatus err;
21086 uint32_t insCount;
21087 unsigned int loopDepth;
21088 const char * cmd;
21089 const char * end;
21090 const char * next;
21091 MDNSCInstruction * program = NULL;
21092 uint32_t loopStart[ kMaxLoopDepth ];
21093
21094 insCount = 0;
21095 for( cmd = inProgramStr; *cmd; cmd = next )
21096 {
21097 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21098 require_action_quiet( end != cmd, exit, err = kMalformedErr );
21099 next = ( *end == ';' ) ? ( end + 1 ) : end;
21100 ++insCount;
21101 }
21102
21103 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
21104 require_action( program, exit, err = kNoMemoryErr );
21105
21106 insCount = 0;
21107 loopDepth = 0;
21108 for( cmd = inProgramStr; *cmd; cmd = next )
21109 {
21110 size_t cmdLen;
21111 const char * ptr;
21112 const char * arg;
21113 size_t argLen;
21114 uint32_t value;
21115 MDNSCInstruction * const ins = &program[ insCount ];
21116
21117 while( isspace_safe( *cmd ) ) ++cmd;
21118 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
21119 next = ( *end == ';' ) ? ( end + 1 ) : end;
21120
21121 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21122 cmdLen = (size_t)( ptr - cmd );
21123
21124 // Done statement
21125
21126 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
21127 {
21128 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21129 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21130
21131 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
21132
21133 ins->opcode = kMDNSColliderOpCode_LoopPop;
21134 ins->operand = loopStart[ --loopDepth ];
21135 }
21136
21137 // Loop command
21138
21139 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
21140 {
21141 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21142 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21143 require_noerr_quiet( err, exit );
21144 require_action_quiet( value > 0, exit, err = kValueErr );
21145
21146 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21147 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21148
21149 ins->opcode = kMDNSColliderOpCode_LoopPush;
21150 ins->operand = value;
21151
21152 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
21153 loopStart[ loopDepth++ ] = insCount + 1;
21154 }
21155
21156 // Probes command
21157
21158 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
21159 {
21160 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21161 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
21162 argLen = (size_t)( ptr - arg );
21163 if( argLen > 0 )
21164 {
21165 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
21166 require_noerr_quiet( err, exit );
21167 }
21168 else
21169 {
21170 value = 0;
21171 }
21172
21173 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21174 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21175
21176 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
21177 ins->operand = value;
21178 }
21179
21180 // Send command
21181
21182 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
21183 {
21184 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21185 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21186
21187 ins->opcode = kMDNSColliderOpCode_Send;
21188 }
21189
21190 // Wait command
21191
21192 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
21193 {
21194 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
21195 err = DecimalTextToUInt32( arg, end, &value, &ptr );
21196 require_noerr_quiet( err, exit );
21197
21198 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
21199 require_action_quiet( ptr == end, exit, err = kMalformedErr );
21200
21201 ins->opcode = kMDNSColliderOpCode_Wait;
21202 ins->operand = value;
21203 }
21204
21205 // Unrecognized command
21206
21207 else
21208 {
21209 err = kCommandErr;
21210 goto exit;
21211 }
21212 ++insCount;
21213 }
21214 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
21215
21216 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
21217
21218 FreeNullSafe( me->program );
21219 me->program = program;
21220 program = NULL;
21221 err = kNoErr;
21222
21223 exit:
21224 FreeNullSafe( program );
21225 return( err );
21226 }
21227
21228 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
21229 {
21230 OSStatus err;
21231 const char * ptr;
21232 const char * const end = &inString[ inLen ];
21233 uint32_t bitmap;
21234 int index;
21235
21236 bitmap = 0;
21237 index = 0;
21238 ptr = inString;
21239 while( ptr < end )
21240 {
21241 int c, count;
21242 MDNSColliderProbeAction action;
21243
21244 c = *ptr++;
21245 if( isdigit_safe( c ) )
21246 {
21247 count = 0;
21248 do
21249 {
21250 count = ( count * 10 ) + ( c - '0' );
21251 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
21252 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
21253 c = *ptr++;
21254
21255 } while( isdigit_safe( c ) );
21256 require_action_quiet( count > 0, exit, err = kCountErr );
21257 }
21258 else
21259 {
21260 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
21261 count = 1;
21262 }
21263
21264 switch( c )
21265 {
21266 case 'n': action = kMDNSColliderProbeAction_None; break;
21267 case 'r': action = kMDNSColliderProbeAction_Respond; break;
21268 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
21269 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
21270 case 'p': action = kMDNSColliderProbeAction_Probe; break;
21271 default: err = kMalformedErr; goto exit;
21272 }
21273 if( ptr < end )
21274 {
21275 c = *ptr++;
21276 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
21277 }
21278 while( count-- > 0 )
21279 {
21280 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
21281 ++index;
21282 }
21283 }
21284
21285 *outBitmap = bitmap;
21286 err = kNoErr;
21287
21288 exit:
21289 return( err );
21290 }
21291
21292 //===========================================================================================================================
21293 // MDNSColliderSetStopHandler
21294 //===========================================================================================================================
21295
21296 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
21297 {
21298 me->stopHandler = inStopHandler;
21299 me->stopContext = inStopContext;
21300 }
21301
21302 //===========================================================================================================================
21303 // MDNSColliderSetRecord
21304 //===========================================================================================================================
21305
21306 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21307 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21308 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21309
21310 static OSStatus
21311 MDNSColliderSetRecord(
21312 MDNSColliderRef me,
21313 const uint8_t * inName,
21314 uint16_t inType,
21315 const void * inRDataPtr,
21316 size_t inRDataLen )
21317 {
21318 OSStatus err;
21319 DataBuffer msgDB;
21320 DNSHeader header;
21321 uint8_t * targetPtr = NULL;
21322 size_t targetLen;
21323 uint8_t * responsePtr = NULL;
21324 size_t responseLen;
21325 uint8_t * probePtr = NULL;
21326 size_t probeLen;
21327
21328 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
21329
21330 err = DomainNameDup( inName, &targetPtr, &targetLen );
21331 require_noerr_quiet( err, exit );
21332
21333 // Create response message.
21334
21335 memset( &header, 0, sizeof( header ) );
21336 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
21337 DNSHeaderSetAnswerCount( &header, 1 );
21338
21339 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21340 require_noerr( err, exit );
21341
21342 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kRRClassCacheFlushBit,
21343 1976, inRDataPtr, inRDataLen );
21344 require_noerr( err, exit );
21345
21346 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
21347 require_noerr( err, exit );
21348
21349 // Create probe message.
21350
21351 memset( &header, 0, sizeof( header ) );
21352 DNSHeaderSetQuestionCount( &header, 2 );
21353 DNSHeaderSetAuthorityCount( &header, 1 );
21354
21355 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
21356 require_noerr( err, exit );
21357
21358 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
21359 require_noerr( err, exit );
21360
21361 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
21362 kDNSServiceType_NULL, kDNSServiceClass_IN );
21363 require_noerr( err, exit );
21364
21365 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
21366 1976, inRDataPtr, inRDataLen );
21367 require_noerr( err, exit );
21368
21369 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
21370 require_noerr( err, exit );
21371
21372 FreeNullSafe( me->target );
21373 me->target = targetPtr;
21374 targetPtr = NULL;
21375
21376 FreeNullSafe( me->responsePtr );
21377 me->responsePtr = responsePtr;
21378 me->responseLen = responseLen;
21379 responsePtr = NULL;
21380
21381 FreeNullSafe( me->probePtr );
21382 me->probePtr = probePtr;
21383 me->probeLen = probeLen;
21384 probePtr = NULL;
21385
21386 exit:
21387 DataBuffer_Free( &msgDB );
21388 FreeNullSafe( targetPtr );
21389 FreeNullSafe( responsePtr );
21390 FreeNullSafe( probePtr );
21391 return( err );
21392 }
21393
21394 //===========================================================================================================================
21395 // _MDNSColliderStop
21396 //===========================================================================================================================
21397
21398 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
21399 {
21400 dispatch_source_forget( &me->waitTimer );
21401 dispatch_source_forget( &me->readSourceV4 );
21402 dispatch_source_forget( &me->readSourceV6 );
21403 me->sockV4 = kInvalidSocketRef;
21404 me->sockV6 = kInvalidSocketRef;
21405
21406 if( !me->stopped )
21407 {
21408 me->stopped = true;
21409 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
21410 CFRelease( me );
21411 }
21412 }
21413
21414 //===========================================================================================================================
21415 // _MDNSColliderReadHandler
21416 //===========================================================================================================================
21417
21418 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
21419 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
21420
21421 static void _MDNSColliderReadHandler( void *inContext )
21422 {
21423 OSStatus err;
21424 struct timeval now;
21425 SocketContext * const sockCtx = (SocketContext *) inContext;
21426 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
21427 size_t msgLen;
21428 sockaddr_ip sender;
21429 const DNSHeader * hdr;
21430 const uint8_t * ptr;
21431 const struct sockaddr * dest;
21432 int probeFound, probeIsQU;
21433 unsigned int qCount, i;
21434 MDNSColliderProbeAction action;
21435
21436 gettimeofday( &now, NULL );
21437
21438 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
21439 NULL, NULL, NULL, NULL );
21440 require_noerr( err, exit );
21441
21442 require_quiet( msgLen >= kDNSHeaderLength, exit );
21443 hdr = (const DNSHeader *) me->msgBuf;
21444
21445 probeFound = false;
21446 probeIsQU = false;
21447 qCount = DNSHeaderGetQuestionCount( hdr );
21448 ptr = (const uint8_t *) &hdr[ 1 ];
21449 for( i = 0; i < qCount; ++i )
21450 {
21451 uint16_t qtype, qclass;
21452 uint8_t qname[ kDomainNameLengthMax ];
21453
21454 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
21455 require_noerr_quiet( err, exit );
21456
21457 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
21458 DomainNameEqual( qname, kMDNSColliderDummyName ) )
21459 {
21460 probeFound = false;
21461 break;
21462 }
21463
21464 if( qtype != kDNSServiceType_ANY ) continue;
21465 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
21466 if( !DomainNameEqual( qname, me->target ) ) continue;
21467
21468 if( !probeFound )
21469 {
21470 probeFound = true;
21471 probeIsQU = ( qclass & kQClassUnicastResponseBit ) ? true : false;
21472 }
21473 }
21474 require_quiet( probeFound, exit );
21475
21476 ++me->probeCount;
21477 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
21478
21479 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21480 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
21481
21482 if( ( action == kMDNSColliderProbeAction_Respond ) ||
21483 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
21484 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21485 {
21486 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
21487 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
21488 {
21489 dest = &sender.sa;
21490 }
21491 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
21492 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
21493 {
21494 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21495 }
21496
21497 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
21498 require_noerr( err, exit );
21499 }
21500 else if( action == kMDNSColliderProbeAction_Probe )
21501 {
21502 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21503
21504 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
21505 require_noerr( err, exit );
21506 }
21507
21508 exit:
21509 return;
21510 }
21511
21512 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
21513 {
21514 MDNSColliderProbeAction action;
21515
21516 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
21517 {
21518 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
21519 kMDNSColliderProbeActionBits_Mask;
21520 }
21521 else
21522 {
21523 action = kMDNSColliderProbeAction_None;
21524 }
21525 return( action );
21526 }
21527
21528 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
21529 {
21530 switch( inAction )
21531 {
21532 case kMDNSColliderProbeAction_None: return( "None" );
21533 case kMDNSColliderProbeAction_Respond: return( "Respond" );
21534 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
21535 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
21536 case kMDNSColliderProbeAction_Probe: return( "Probe" );
21537 default: return( "???" );
21538 }
21539 }
21540
21541 //===========================================================================================================================
21542 // _MDNSColliderExecuteProgram
21543 //===========================================================================================================================
21544
21545 static void _MDNSColliderExecuteProgram( void *inContext )
21546 {
21547 OSStatus err;
21548 MDNSColliderRef const me = (MDNSColliderRef) inContext;
21549 int stop;
21550
21551 dispatch_forget( &me->waitTimer );
21552
21553 stop = false;
21554 for( ;; )
21555 {
21556 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
21557 uint32_t waitMs;
21558
21559 switch( ins->opcode )
21560 {
21561 case kMDNSColliderOpCode_Send:
21562 if( IsValidSocket( me->sockV4 ) )
21563 {
21564 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
21565 require_noerr( err, exit );
21566 }
21567 if( IsValidSocket( me->sockV6 ) )
21568 {
21569 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
21570 require_noerr( err, exit );
21571 }
21572 break;
21573
21574 case kMDNSColliderOpCode_Wait:
21575 waitMs = ins->operand;
21576 if( waitMs > 0 )
21577 {
21578 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
21579 _MDNSColliderExecuteProgram, me, &me->waitTimer );
21580 require_noerr( err, exit );
21581 dispatch_resume( me->waitTimer );
21582 goto exit;
21583 }
21584 break;
21585
21586 case kMDNSColliderOpCode_SetProbeActions:
21587 me->probeCount = 0;
21588 me->probeActionMap = ins->operand;
21589 break;
21590
21591 case kMDNSColliderOpCode_LoopPush:
21592 check( me->loopDepth < kMaxLoopDepth );
21593 me->loopCounts[ me->loopDepth++ ] = ins->operand;
21594 break;
21595
21596 case kMDNSColliderOpCode_LoopPop:
21597 check( me->loopDepth > 0 );
21598 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
21599 {
21600 me->pc = ins->operand;
21601 }
21602 else
21603 {
21604 --me->loopDepth;
21605 }
21606 break;
21607
21608 case kMDNSColliderOpCode_Exit:
21609 stop = true;
21610 err = kNoErr;
21611 goto exit;
21612
21613 default:
21614 dlogassert( "Unhandled opcode %u\n", ins->opcode );
21615 err = kCommandErr;
21616 goto exit;
21617 }
21618 }
21619
21620 exit:
21621 if( err || stop ) _MDNSColliderStop( me, err );
21622 }
21623
21624 //===========================================================================================================================
21625 // _MDNSColliderSendResponse
21626 //===========================================================================================================================
21627
21628 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21629 {
21630 OSStatus err;
21631 ssize_t n;
21632
21633 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
21634 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
21635 return( err );
21636 }
21637
21638 //===========================================================================================================================
21639 // _MDNSColliderSendProbe
21640 //===========================================================================================================================
21641
21642 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
21643 {
21644 OSStatus err;
21645 ssize_t n;
21646
21647 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
21648 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
21649 return( err );
21650 }
21651
21652 //===========================================================================================================================
21653 // ServiceBrowserCreate
21654 //===========================================================================================================================
21655
21656 typedef struct SBDomain SBDomain;
21657 typedef struct SBServiceType SBServiceType;
21658 typedef struct SBServiceBrowse SBServiceBrowse;
21659 typedef struct SBServiceInstance SBServiceInstance;
21660 typedef struct SBIPAddress SBIPAddress;
21661
21662 struct ServiceBrowserPrivate
21663 {
21664 CFRuntimeBase base; // CF object base.
21665 dispatch_queue_t queue; // Queue for service browser's events.
21666 DNSServiceRef connection; // Shared connection for DNS-SD ops.
21667 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
21668 char * domain; // If non-null, then browsing is limited to this domain.
21669 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
21670 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
21671 void * userContext; // User's callback context.
21672 SBDomain * domainList; // List of domains and their browse results.
21673 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
21674 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
21675 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
21676 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21677 // use the "any" interface.
21678 };
21679
21680 struct SBDomain
21681 {
21682 SBDomain * next; // Next domain object in list.
21683 ServiceBrowserRef browser; // Pointer to parent service browser.
21684 char * name; // Name of the domain.
21685 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21686 SBServiceType * typeList; // List of service types to browse for in this domain.
21687 };
21688
21689 struct SBServiceType
21690 {
21691 SBServiceType * next; // Next service type object in list.
21692 char * name; // Name of the service type.
21693 SBServiceBrowse * browseList; // List of browses for this service type.
21694 };
21695
21696 struct SBServiceBrowse
21697 {
21698 SBServiceBrowse * next; // Next browse object in list.
21699 ServiceBrowserRef browser; // Pointer to parent service browser.
21700 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
21701 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
21702 uint64_t startTicks; // Value of UpTicks() when the browse op began.
21703 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
21704 };
21705
21706 struct SBServiceInstance
21707 {
21708 SBServiceInstance * next; // Next service instance object in list.
21709 ServiceBrowserRef browser; // Pointer to parent service browser.
21710 char * name; // Name of the service instance.
21711 char * fqdn; // Fully qualified domain name of service instance (for logging/debugging).
21712 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
21713 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
21714 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
21715 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
21716 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
21717 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
21718 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
21719 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
21720 size_t txtLen; // Length of service instance's TXT record data.
21721 DNSServiceRef getAddrInfo; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21722 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21723 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
21724 };
21725
21726 struct SBIPAddress
21727 {
21728 SBIPAddress * next; // Next IP address object in list.
21729 sockaddr_ip sip; // IPv4 or IPv6 address.
21730 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
21731 };
21732
21733 typedef struct
21734 {
21735 SBRDomain * domainList; // List of domains in which services were found.
21736 int32_t refCount; // This object's reference count.
21737
21738 } ServiceBrowserResultsPrivate;
21739
21740 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
21741 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
21742 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
21743 static void _ServiceBrowserTimerHandler( void *inContext );
21744 static void DNSSD_API
21745 _ServiceBrowserDomainsQueryCallback(
21746 DNSServiceRef inSDRef,
21747 DNSServiceFlags inFlags,
21748 uint32_t inInterfaceIndex,
21749 DNSServiceErrorType inError,
21750 const char * inFullName,
21751 uint16_t inType,
21752 uint16_t inClass,
21753 uint16_t inRDataLen,
21754 const void * inRDataPtr,
21755 uint32_t inTTL,
21756 void * inContext );
21757 static void DNSSD_API
21758 _ServiceBrowserServicesQueryCallback(
21759 DNSServiceRef inSDRef,
21760 DNSServiceFlags inFlags,
21761 uint32_t inInterfaceIndex,
21762 DNSServiceErrorType inError,
21763 const char * inFullName,
21764 uint16_t inType,
21765 uint16_t inClass,
21766 uint16_t inRDataLen,
21767 const void * inRDataPtr,
21768 uint32_t inTTL,
21769 void * inContext );
21770 static void DNSSD_API
21771 _ServiceBrowserBrowseCallback(
21772 DNSServiceRef inSDRef,
21773 DNSServiceFlags inFlags,
21774 uint32_t inInterfaceIndex,
21775 DNSServiceErrorType inError,
21776 const char * inName,
21777 const char * inRegType,
21778 const char * inDomain,
21779 void * inContext );
21780 static void DNSSD_API
21781 _ServiceBrowserResolveCallback(
21782 DNSServiceRef inSDRef,
21783 DNSServiceFlags inFlags,
21784 uint32_t inInterfaceIndex,
21785 DNSServiceErrorType inError,
21786 const char * inFullName,
21787 const char * inHostname,
21788 uint16_t inPort,
21789 uint16_t inTXTLen,
21790 const unsigned char * inTXTPtr,
21791 void * inContext );
21792 static void DNSSD_API
21793 _ServiceBrowserGAICallback(
21794 DNSServiceRef inSDRef,
21795 DNSServiceFlags inFlags,
21796 uint32_t inInterfaceIndex,
21797 DNSServiceErrorType inError,
21798 const char * inHostname,
21799 const struct sockaddr * inSockAddr,
21800 uint32_t inTTL,
21801 void * inContext );
21802 static OSStatus
21803 _ServiceBrowserAddServiceType(
21804 ServiceBrowserRef inBrowser,
21805 SBDomain * inDomain,
21806 const char * inName,
21807 uint32_t inIfIndex );
21808 static OSStatus
21809 _ServiceBrowserRemoveServiceType(
21810 ServiceBrowserRef inBrowser,
21811 SBDomain * inDomain,
21812 const char * inName,
21813 uint32_t inIfIndex );
21814 static OSStatus
21815 _ServiceBrowserAddServiceInstance(
21816 ServiceBrowserRef inBrowser,
21817 SBServiceBrowse * inBrowse,
21818 uint32_t inIfIndex,
21819 const char * inName,
21820 const char * inRegType,
21821 const char * inDomain,
21822 uint64_t inDiscoverTimeUs );
21823 static OSStatus
21824 _ServiceBrowserRemoveServiceInstance(
21825 ServiceBrowserRef inBrowser,
21826 SBServiceBrowse * inBrowse,
21827 const char * inName,
21828 uint32_t inIfIndex );
21829 static OSStatus
21830 _ServiceBrowserAddIPAddress(
21831 ServiceBrowserRef inBrowser,
21832 SBServiceInstance * inInstance,
21833 const struct sockaddr * inSockAddr,
21834 uint64_t inResolveTimeUs );
21835 static OSStatus
21836 _ServiceBrowserRemoveIPAddress(
21837 ServiceBrowserRef inBrowser,
21838 SBServiceInstance * inInstance,
21839 const struct sockaddr * inSockAddr );
21840 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
21841 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
21842 static void _SBDomainFree( SBDomain *inDomain );
21843 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
21844 static void _SBServiceTypeFree( SBServiceType *inType );
21845 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
21846 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
21847 static OSStatus
21848 _SBServiceInstanceCreate(
21849 const char * inName,
21850 const char * inType,
21851 const char * inDomain,
21852 uint32_t inIfIndex,
21853 uint64_t inDiscoverTimeUs,
21854 ServiceBrowserRef inBrowser,
21855 SBServiceInstance ** outInstance );
21856 static void _SBServiceInstanceFree( SBServiceInstance *inInstance );
21857 static OSStatus
21858 _SBIPAddressCreate(
21859 const struct sockaddr * inSockAddr,
21860 uint64_t inResolveTimeUs,
21861 SBIPAddress ** outIPAddress );
21862 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
21863 static void _SBIPAddressFreeList( SBIPAddress *inList );
21864 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
21865 static void _SBRDomainFree( SBRDomain *inDomain );
21866 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
21867 static void _SBRServiceTypeFree( SBRServiceType *inType );
21868 static OSStatus
21869 _SBRServiceInstanceCreate(
21870 const char * inName,
21871 uint32_t inInterfaceIndex,
21872 const char * inHostname,
21873 uint16_t inPort,
21874 const uint8_t * inTXTPtr,
21875 size_t inTXTLen,
21876 uint64_t inDiscoverTimeUs,
21877 uint64_t inResolveTimeUs,
21878 SBRServiceInstance ** outInstance );
21879 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
21880 static OSStatus
21881 _SBRIPAddressCreate(
21882 const struct sockaddr * inSockAddr,
21883 uint64_t inResolveTimeUs,
21884 SBRIPAddress ** outIPAddress );
21885 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
21886
21887 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21888
21889 CF_CLASS_DEFINE( ServiceBrowser );
21890
21891 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
21892 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21893
21894 static OSStatus
21895 ServiceBrowserCreate(
21896 dispatch_queue_t inQueue,
21897 uint32_t inInterfaceIndex,
21898 const char * inDomain,
21899 unsigned int inBrowseTimeSecs,
21900 Boolean inIncludeAWDL,
21901 ServiceBrowserRef * outBrowser )
21902 {
21903 OSStatus err;
21904 ServiceBrowserRef obj;
21905
21906 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
21907
21908 ReplaceDispatchQueue( &obj->queue, inQueue );
21909 obj->ifIndex = inInterfaceIndex;
21910 if( inDomain )
21911 {
21912 obj->domain = strdup( inDomain );
21913 require_action( obj->domain, exit, err = kNoMemoryErr );
21914 }
21915 obj->browseTimeSecs = inBrowseTimeSecs;
21916 obj->includeAWDL = inIncludeAWDL;
21917
21918 *outBrowser = obj;
21919 obj = NULL;
21920 err = kNoErr;
21921
21922 exit:
21923 CFReleaseNullSafe( obj );
21924 return( err );
21925 }
21926
21927 //===========================================================================================================================
21928 // _ServiceBrowserFinalize
21929 //===========================================================================================================================
21930
21931 static void _ServiceBrowserFinalize( CFTypeRef inObj )
21932 {
21933 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
21934 StringListItem * serviceType;
21935
21936 dispatch_forget( &me->queue );
21937 check( !me->connection );
21938 check( !me->domainsQuery );
21939 ForgetMem( &me->domain );
21940 while( ( serviceType = me->serviceTypeList ) != NULL )
21941 {
21942 me->serviceTypeList = serviceType->next;
21943 ForgetMem( &serviceType->str );
21944 free( serviceType );
21945 }
21946 check( !me->domainList );
21947 check( !me->stopTimer );
21948 }
21949
21950 //===========================================================================================================================
21951 // ServiceBrowserStart
21952 //===========================================================================================================================
21953
21954 static void _ServiceBrowserStart( void *inContext );
21955
21956 static void ServiceBrowserStart( ServiceBrowserRef me )
21957 {
21958 CFRetain( me );
21959 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
21960 }
21961
21962 static void _ServiceBrowserStart( void *inContext )
21963 {
21964 OSStatus err;
21965 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
21966
21967 err = DNSServiceCreateConnection( &me->connection );
21968 require_noerr( err, exit );
21969
21970 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
21971 require_noerr( err, exit );
21972
21973 if( me->domain )
21974 {
21975 err = _ServiceBrowserAddDomain( me, me->domain );
21976 require_noerr( err, exit );
21977 }
21978 else
21979 {
21980 DNSServiceRef sdRef;
21981 const char * const recordName = "b._dns-sd._udp.local.";
21982 const uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
21983
21984 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21985 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21986
21987 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
21988
21989 sdRef = me->connection;
21990 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
21991 kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
21992 require_noerr( err, exit );
21993
21994 me->domainsQuery = sdRef;
21995 }
21996
21997 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
21998 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
21999 require_noerr( err, exit );
22000 dispatch_resume( me->stopTimer );
22001
22002 exit:
22003 if( err ) _ServiceBrowserStop( me, err );
22004 }
22005
22006 //===========================================================================================================================
22007 // ServiceBrowserAddServiceType
22008 //===========================================================================================================================
22009
22010 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
22011 {
22012 OSStatus err;
22013 StringListItem * item;
22014 StringListItem ** itemPtr;
22015 StringListItem * newItem = NULL;
22016
22017 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
22018 {
22019 if( strcmp( item->str, inServiceType ) == 0 ) break;
22020 }
22021 if( !item )
22022 {
22023 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
22024 require_action( newItem, exit, err = kNoMemoryErr );
22025
22026 newItem->str = strdup( inServiceType );
22027 require_action( newItem->str, exit, err = kNoMemoryErr );
22028
22029 *itemPtr = newItem;
22030 newItem = NULL;
22031 }
22032 err = kNoErr;
22033
22034 exit:
22035 FreeNullSafe( newItem );
22036 return( err );
22037 }
22038
22039 //===========================================================================================================================
22040 // ServiceBrowserSetCallback
22041 //===========================================================================================================================
22042
22043 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
22044 {
22045 me->userCallback = inCallback;
22046 me->userContext = inContext;
22047 }
22048
22049 //===========================================================================================================================
22050 // ServiceBrowserResultsRetain
22051 //===========================================================================================================================
22052
22053 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
22054 {
22055 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22056
22057 atomic_add_32( &results->refCount, 1 );
22058 }
22059
22060 //===========================================================================================================================
22061 // ServiceBrowserResultsRelease
22062 //===========================================================================================================================
22063
22064 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
22065 {
22066 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
22067 SBRDomain * domain;
22068
22069 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
22070 {
22071 while( ( domain = inResults->domainList ) != NULL )
22072 {
22073 inResults->domainList = domain->next;
22074 _SBRDomainFree( domain );
22075 }
22076 free( inResults );
22077 }
22078 }
22079
22080 //===========================================================================================================================
22081 // _ServiceBrowserStop
22082 //===========================================================================================================================
22083
22084 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
22085 {
22086 OSStatus err;
22087 SBDomain * d;
22088 SBServiceType * t;
22089 SBServiceBrowse * b;
22090 SBServiceInstance * i;
22091
22092 dispatch_source_forget( &me->stopTimer );
22093 DNSServiceForget( &me->domainsQuery );
22094 for( d = me->domainList; d; d = d->next )
22095 {
22096 DNSServiceForget( &d->servicesQuery );
22097 for( t = d->typeList; t; t = t->next )
22098 {
22099 for( b = t->browseList; b; b = b->next )
22100 {
22101 DNSServiceForget( &b->browse );
22102 for( i = b->instanceList; i; i = i->next )
22103 {
22104 DNSServiceForget( &i->resolve );
22105 DNSServiceForget( &i->getAddrInfo );
22106 }
22107 }
22108 }
22109 }
22110 DNSServiceForget( &me->connection );
22111
22112 if( me->userCallback )
22113 {
22114 ServiceBrowserResults * results = NULL;
22115
22116 err = _ServiceBrowserCreateResults( me, &results );
22117 if( !err ) err = inError;
22118
22119 me->userCallback( results, err, me->userContext );
22120 me->userCallback = NULL;
22121 me->userContext = NULL;
22122 if( results ) ServiceBrowserResultsRelease( results );
22123 }
22124
22125 while( ( d = me->domainList ) != NULL )
22126 {
22127 me->domainList = d->next;
22128 _SBDomainFree( d );
22129 }
22130 CFRelease( me );
22131 }
22132
22133 //===========================================================================================================================
22134 // _ServiceBrowserAddDomain
22135 //===========================================================================================================================
22136
22137 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
22138 {
22139 OSStatus err;
22140 SBDomain * domain;
22141 SBDomain ** domainPtr;
22142 SBDomain * newDomain = NULL;
22143
22144 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22145 {
22146 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
22147 }
22148 require_action_quiet( !domain, exit, err = kDuplicateErr );
22149
22150 err = _SBDomainCreate( inDomain, me, &newDomain );
22151 require_noerr_quiet( err, exit );
22152
22153 if( me->serviceTypeList )
22154 {
22155 const StringListItem * item;
22156
22157 for( item = me->serviceTypeList; item; item = item->next )
22158 {
22159 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
22160 if( err == kDuplicateErr ) err = kNoErr;
22161 require_noerr( err, exit );
22162 }
22163 }
22164 else
22165 {
22166 char * recordName;
22167 DNSServiceRef sdRef;
22168 DNSServiceFlags flags;
22169
22170 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22171 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22172
22173 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
22174 require_action( recordName, exit, err = kNoMemoryErr );
22175
22176 flags = kDNSServiceFlagsShareConnection;
22177 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22178
22179 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
22180
22181 sdRef = newDomain->browser->connection;
22182 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
22183 _ServiceBrowserServicesQueryCallback, newDomain );
22184 free( recordName );
22185 require_noerr( err, exit );
22186
22187 newDomain->servicesQuery = sdRef;
22188 }
22189
22190 *domainPtr = newDomain;
22191 newDomain = NULL;
22192 err = kNoErr;
22193
22194 exit:
22195 if( newDomain ) _SBDomainFree( newDomain );
22196 return( err );
22197 }
22198
22199 //===========================================================================================================================
22200 // _ServiceBrowserRemoveDomain
22201 //===========================================================================================================================
22202
22203 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
22204 {
22205 OSStatus err;
22206 SBDomain * domain;
22207 SBDomain ** domainPtr;
22208
22209 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
22210 {
22211 if( strcasecmp( domain->name, inName ) == 0 ) break;
22212 }
22213
22214 if( domain )
22215 {
22216 *domainPtr = domain->next;
22217 _SBDomainFree( domain );
22218 err = kNoErr;
22219 }
22220 else
22221 {
22222 err = kNotFoundErr;
22223 }
22224
22225 return( err );
22226 }
22227
22228 //===========================================================================================================================
22229 // _ServiceBrowserTimerHandler
22230 //===========================================================================================================================
22231
22232 static void _ServiceBrowserTimerHandler( void *inContext )
22233 {
22234 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22235
22236 _ServiceBrowserStop( me, kNoErr );
22237 }
22238
22239 //===========================================================================================================================
22240 // _ServiceBrowserDomainsQueryCallback
22241 //===========================================================================================================================
22242
22243 static void DNSSD_API
22244 _ServiceBrowserDomainsQueryCallback(
22245 DNSServiceRef inSDRef,
22246 DNSServiceFlags inFlags,
22247 uint32_t inInterfaceIndex,
22248 DNSServiceErrorType inError,
22249 const char * inFullName,
22250 uint16_t inType,
22251 uint16_t inClass,
22252 uint16_t inRDataLen,
22253 const void * inRDataPtr,
22254 uint32_t inTTL,
22255 void * inContext )
22256 {
22257 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
22258 OSStatus err;
22259 char domainStr[ kDNSServiceMaxDomainName ];
22260
22261 Unused( inSDRef );
22262 Unused( inClass );
22263 Unused( inTTL );
22264
22265 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22266 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22267 !inError, inError );
22268
22269 require_noerr( inError, exit );
22270
22271 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
22272 require_noerr( err, exit );
22273
22274 if( inFlags & kDNSServiceFlagsAdd )
22275 {
22276 err = _ServiceBrowserAddDomain( me, domainStr );
22277 if( err == kDuplicateErr ) err = kNoErr;
22278 require_noerr( err, exit );
22279 }
22280 else
22281 {
22282 err = _ServiceBrowserRemoveDomain( me, domainStr );
22283 if( err == kNotFoundErr ) err = kNoErr;
22284 require_noerr( err, exit );
22285 }
22286
22287 exit:
22288 return;
22289 }
22290
22291 //===========================================================================================================================
22292 // _ServiceBrowserServicesQueryCallback
22293 //===========================================================================================================================
22294
22295 static void DNSSD_API
22296 _ServiceBrowserServicesQueryCallback(
22297 DNSServiceRef inSDRef,
22298 DNSServiceFlags inFlags,
22299 uint32_t inInterfaceIndex,
22300 DNSServiceErrorType inError,
22301 const char * inFullName,
22302 uint16_t inType,
22303 uint16_t inClass,
22304 uint16_t inRDataLen,
22305 const void * inRDataPtr,
22306 uint32_t inTTL,
22307 void * inContext )
22308 {
22309 OSStatus err;
22310 SBDomain * const domain = (SBDomain *) inContext;
22311 ServiceBrowserRef const me = domain->browser;
22312 const uint8_t * src;
22313 const uint8_t * end;
22314 uint8_t * dst;
22315 int i;
22316 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
22317 char serviceTypeStr[ kDNSServiceMaxDomainName ];
22318
22319 Unused( inSDRef );
22320 Unused( inTTL );
22321 Unused( inClass );
22322
22323 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22324 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
22325 !inError, inError );
22326
22327 require_noerr( inError, exit );
22328
22329 check( inType == kDNSServiceType_PTR );
22330 check( inClass == kDNSServiceClass_IN );
22331
22332 // The first two labels of the domain name in the RDATA describe a service type.
22333 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22334
22335 src = (const uint8_t *) inRDataPtr;
22336 end = src + inRDataLen;
22337 dst = serviceType;
22338 for( i = 0; i < 2; ++i )
22339 {
22340 size_t labelLen;
22341
22342 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
22343
22344 labelLen = *src;
22345 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
22346 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
22347
22348 memcpy( dst, src, 1 + labelLen );
22349 src += 1 + labelLen;
22350 dst += 1 + labelLen;
22351 }
22352 *dst = 0;
22353
22354 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
22355 require_noerr( err, exit );
22356
22357 if( inFlags & kDNSServiceFlagsAdd )
22358 {
22359 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22360 if( err == kDuplicateErr ) err = kNoErr;
22361 require_noerr( err, exit );
22362 }
22363 else
22364 {
22365 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
22366 if( err == kNotFoundErr ) err = kNoErr;
22367 require_noerr( err, exit );
22368 }
22369
22370 exit:
22371 return;
22372 }
22373
22374 //===========================================================================================================================
22375 // _ServiceBrowserBrowseCallback
22376 //===========================================================================================================================
22377
22378 static void DNSSD_API
22379 _ServiceBrowserBrowseCallback(
22380 DNSServiceRef inSDRef,
22381 DNSServiceFlags inFlags,
22382 uint32_t inInterfaceIndex,
22383 DNSServiceErrorType inError,
22384 const char * inName,
22385 const char * inRegType,
22386 const char * inDomain,
22387 void * inContext )
22388 {
22389 OSStatus err;
22390 const uint64_t nowTicks = UpTicks();
22391 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
22392 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
22393
22394 Unused( inSDRef );
22395
22396 sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22397 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
22398
22399 require_noerr( inError, exit );
22400
22401 if( inFlags & kDNSServiceFlagsAdd )
22402 {
22403 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
22404 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
22405 if( err == kDuplicateErr ) err = kNoErr;
22406 require_noerr( err, exit );
22407 }
22408 else
22409 {
22410 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
22411 if( err == kNotFoundErr ) err = kNoErr;
22412 require_noerr( err, exit );
22413 }
22414
22415 exit:
22416 return;
22417 }
22418
22419 //===========================================================================================================================
22420 // _ServiceBrowserResolveCallback
22421 //===========================================================================================================================
22422
22423 static void DNSSD_API
22424 _ServiceBrowserResolveCallback(
22425 DNSServiceRef inSDRef,
22426 DNSServiceFlags inFlags,
22427 uint32_t inInterfaceIndex,
22428 DNSServiceErrorType inError,
22429 const char * inFullName,
22430 const char * inHostname,
22431 uint16_t inPort,
22432 uint16_t inTXTLen,
22433 const unsigned char * inTXTPtr,
22434 void * inContext )
22435 {
22436 OSStatus err;
22437 const uint64_t nowTicks = UpTicks();
22438 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22439 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22440
22441 Unused( inSDRef );
22442 Unused( inFlags );
22443
22444 sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22445 (int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
22446
22447 require_noerr( inError, exit );
22448
22449 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
22450 {
22451 FreeNullSafe( instance->txtPtr );
22452 instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
22453 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
22454
22455 instance->txtLen = inTXTLen;
22456 }
22457
22458 instance->port = ntohs( inPort );
22459
22460 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
22461 {
22462 DNSServiceRef sdRef;
22463
22464 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
22465
22466 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
22467 require_noerr( err, exit );
22468
22469 DNSServiceForget( &instance->getAddrInfo );
22470 ForgetSBIPAddressList( &instance->ipaddrList );
22471
22472 sb_ulog( kLogLevelTrace, "Starting GetAddrInfo on interface %d for %s",
22473 (int32_t) instance->ifIndex, instance->hostname );
22474
22475 sdRef = me->connection;
22476 instance->gaiStartTicks = UpTicks();
22477 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
22478 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback, instance );
22479 require_noerr( err, exit );
22480
22481 instance->getAddrInfo = sdRef;
22482 }
22483
22484 exit:
22485 return;
22486 }
22487
22488 //===========================================================================================================================
22489 // _ServiceBrowserGAICallback
22490 //===========================================================================================================================
22491
22492 static void DNSSD_API
22493 _ServiceBrowserGAICallback(
22494 DNSServiceRef inSDRef,
22495 DNSServiceFlags inFlags,
22496 uint32_t inInterfaceIndex,
22497 DNSServiceErrorType inError,
22498 const char * inHostname,
22499 const struct sockaddr * inSockAddr,
22500 uint32_t inTTL,
22501 void * inContext )
22502 {
22503 OSStatus err;
22504 const uint64_t nowTicks = UpTicks();
22505 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
22506 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
22507
22508 Unused( inSDRef );
22509 Unused( inTTL );
22510
22511 sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22512 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
22513 !inError, inError );
22514
22515 require_noerr( inError, exit );
22516
22517 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22518 {
22519 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22520 goto exit;
22521 }
22522
22523 if( inFlags & kDNSServiceFlagsAdd )
22524 {
22525 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
22526 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
22527 if( err == kDuplicateErr ) err = kNoErr;
22528 require_noerr( err, exit );
22529 }
22530 else
22531 {
22532 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
22533 if( err == kNotFoundErr ) err = kNoErr;
22534 require_noerr( err, exit );
22535 }
22536
22537 exit:
22538 return;
22539 }
22540
22541 //===========================================================================================================================
22542 // _ServiceBrowserAddServiceType
22543 //===========================================================================================================================
22544
22545 static OSStatus
22546 _ServiceBrowserAddServiceType(
22547 ServiceBrowserRef me,
22548 SBDomain * inDomain,
22549 const char * inName,
22550 uint32_t inIfIndex )
22551 {
22552 OSStatus err;
22553 SBServiceType * type;
22554 SBServiceType ** typePtr;
22555 SBServiceType * newType = NULL;
22556 SBServiceBrowse * browse;
22557 SBServiceBrowse ** browsePtr;
22558 SBServiceBrowse * newBrowse = NULL;
22559 DNSServiceRef sdRef;
22560 DNSServiceFlags flags;
22561
22562 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22563 {
22564 if( strcasecmp( type->name, inName ) == 0 ) break;
22565 }
22566 if( !type )
22567 {
22568 err = _SBServiceTypeCreate( inName, &newType );
22569 require_noerr_quiet( err, exit );
22570
22571 type = newType;
22572 }
22573
22574 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22575 {
22576 if( browse->ifIndex == inIfIndex ) break;
22577 }
22578 require_action_quiet( !browse, exit, err = kDuplicateErr );
22579
22580 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
22581 require_noerr_quiet( err, exit );
22582
22583 flags = kDNSServiceFlagsShareConnection;
22584 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
22585
22586 sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
22587 (int32_t) newBrowse->ifIndex, type->name, inDomain->name );
22588
22589 sdRef = me->connection;
22590 newBrowse->startTicks = UpTicks();
22591 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
22592 newBrowse );
22593 require_noerr( err, exit );
22594
22595 newBrowse->browse = sdRef;
22596 *browsePtr = newBrowse;
22597 newBrowse = NULL;
22598
22599 if( newType )
22600 {
22601 *typePtr = newType;
22602 newType = NULL;
22603 }
22604
22605 exit:
22606 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
22607 if( newType ) _SBServiceTypeFree( newType );
22608 return( err );
22609 }
22610
22611 //===========================================================================================================================
22612 // _ServiceBrowserRemoveServiceType
22613 //===========================================================================================================================
22614
22615 static OSStatus
22616 _ServiceBrowserRemoveServiceType(
22617 ServiceBrowserRef me,
22618 SBDomain * inDomain,
22619 const char * inName,
22620 uint32_t inIfIndex )
22621 {
22622 OSStatus err;
22623 SBServiceType * type;
22624 SBServiceType ** typePtr;
22625 SBServiceBrowse * browse;
22626 SBServiceBrowse ** browsePtr;
22627
22628 Unused( me );
22629
22630 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
22631 {
22632 if( strcasecmp( type->name, inName ) == 0 ) break;
22633 }
22634 require_action_quiet( type, exit, err = kNotFoundErr );
22635
22636 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
22637 {
22638 if( browse->ifIndex == inIfIndex ) break;
22639 }
22640 require_action_quiet( browse, exit, err = kNotFoundErr );
22641
22642 *browsePtr = browse->next;
22643 _SBServiceBrowseFree( browse );
22644 if( !type->browseList )
22645 {
22646 *typePtr = type->next;
22647 _SBServiceTypeFree( type );
22648 }
22649 err = kNoErr;
22650
22651 exit:
22652 return( err );
22653 }
22654
22655 //===========================================================================================================================
22656 // _ServiceBrowserAddServiceInstance
22657 //===========================================================================================================================
22658
22659 static OSStatus
22660 _ServiceBrowserAddServiceInstance(
22661 ServiceBrowserRef me,
22662 SBServiceBrowse * inBrowse,
22663 uint32_t inIfIndex,
22664 const char * inName,
22665 const char * inRegType,
22666 const char * inDomain,
22667 uint64_t inDiscoverTimeUs )
22668 {
22669 OSStatus err;
22670 DNSServiceRef sdRef;
22671 SBServiceInstance * instance;
22672 SBServiceInstance ** instancePtr;
22673 SBServiceInstance * newInstance = NULL;
22674
22675 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
22676 {
22677 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22678 }
22679 require_action_quiet( !instance, exit, err = kDuplicateErr );
22680
22681 err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
22682 require_noerr_quiet( err, exit );
22683
22684 sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
22685 (int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
22686
22687 sdRef = me->connection;
22688 newInstance->resolveStartTicks = UpTicks();
22689 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
22690 _ServiceBrowserResolveCallback, newInstance );
22691 require_noerr( err, exit );
22692
22693 newInstance->resolve = sdRef;
22694 *instancePtr = newInstance;
22695 newInstance = NULL;
22696
22697 exit:
22698 if( newInstance ) _SBServiceInstanceFree( newInstance );
22699 return( err );
22700 }
22701
22702 //===========================================================================================================================
22703 // _ServiceBrowserRemoveServiceInstance
22704 //===========================================================================================================================
22705
22706 static OSStatus
22707 _ServiceBrowserRemoveServiceInstance(
22708 ServiceBrowserRef me,
22709 SBServiceBrowse * inBrowse,
22710 const char * inName,
22711 uint32_t inIfIndex )
22712 {
22713 OSStatus err;
22714 SBServiceInstance * instance;
22715 SBServiceInstance ** ptr;
22716
22717 Unused( me );
22718
22719 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
22720 {
22721 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
22722 }
22723 require_action_quiet( instance, exit, err = kNotFoundErr );
22724
22725 *ptr = instance->next;
22726 _SBServiceInstanceFree( instance );
22727 err = kNoErr;
22728
22729 exit:
22730 return( err );
22731 }
22732
22733 //===========================================================================================================================
22734 // _ServiceBrowserAddIPAddress
22735 //===========================================================================================================================
22736
22737 static OSStatus
22738 _ServiceBrowserAddIPAddress(
22739 ServiceBrowserRef me,
22740 SBServiceInstance * inInstance,
22741 const struct sockaddr * inSockAddr,
22742 uint64_t inResolveTimeUs )
22743 {
22744 OSStatus err;
22745 SBIPAddress * ipaddr;
22746 SBIPAddress ** ipaddrPtr;
22747 SBIPAddress * newIPAddr = NULL;
22748
22749 Unused( me );
22750
22751 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
22752 {
22753 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
22754 err = kTypeErr;
22755 goto exit;
22756 }
22757
22758 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22759 {
22760 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
22761 }
22762 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
22763
22764 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
22765 require_noerr_quiet( err, exit );
22766
22767 *ipaddrPtr = newIPAddr;
22768 newIPAddr = NULL;
22769 err = kNoErr;
22770
22771 exit:
22772 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
22773 return( err );
22774 }
22775
22776 //===========================================================================================================================
22777 // _ServiceBrowserRemoveIPAddress
22778 //===========================================================================================================================
22779
22780 static OSStatus
22781 _ServiceBrowserRemoveIPAddress(
22782 ServiceBrowserRef me,
22783 SBServiceInstance * inInstance,
22784 const struct sockaddr * inSockAddr )
22785 {
22786 OSStatus err;
22787 SBIPAddress * ipaddr;
22788 SBIPAddress ** ipaddrPtr;
22789
22790 Unused( me );
22791
22792 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
22793 {
22794 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
22795 }
22796 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
22797
22798 *ipaddrPtr = ipaddr->next;
22799 _SBIPAddressFree( ipaddr );
22800 err = kNoErr;
22801
22802 exit:
22803 return( err );
22804 }
22805
22806 //===========================================================================================================================
22807 // _ServiceBrowserCreateResults
22808 //===========================================================================================================================
22809
22810 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
22811 {
22812 OSStatus err;
22813 SBDomain * d;
22814 SBServiceType * t;
22815 SBServiceBrowse * b;
22816 SBServiceInstance * i;
22817 SBIPAddress * a;
22818 ServiceBrowserResultsPrivate * results;
22819 SBRDomain ** domainPtr;
22820
22821 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
22822 require_action( results, exit, err = kNoMemoryErr );
22823
22824 results->refCount = 1;
22825
22826 domainPtr = &results->domainList;
22827 for( d = me->domainList; d; d = d->next )
22828 {
22829 SBRDomain * domain;
22830 SBRServiceType ** typePtr;
22831
22832 err = _SBRDomainCreate( d->name, &domain );
22833 require_noerr_quiet( err, exit );
22834 *domainPtr = domain;
22835 domainPtr = &domain->next;
22836
22837 typePtr = &domain->typeList;
22838 for( t = d->typeList; t; t = t->next )
22839 {
22840 SBRServiceType * type;
22841 SBRServiceInstance ** instancePtr;
22842
22843 err = _SBRServiceTypeCreate( t->name, &type );
22844 require_noerr_quiet( err, exit );
22845 *typePtr = type;
22846 typePtr = &type->next;
22847
22848 instancePtr = &type->instanceList;
22849 for( b = t->browseList; b; b = b->next )
22850 {
22851 for( i = b->instanceList; i; i = i->next )
22852 {
22853 SBRServiceInstance * instance;
22854 SBRIPAddress ** ipaddrPtr;
22855
22856 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
22857 i->discoverTimeUs, i->resolveTimeUs, &instance );
22858 require_noerr_quiet( err, exit );
22859 *instancePtr = instance;
22860 instancePtr = &instance->next;
22861
22862 ipaddrPtr = &instance->ipaddrList;
22863 for( a = i->ipaddrList; a; a = a->next )
22864 {
22865 SBRIPAddress * ipaddr;
22866
22867 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
22868 require_noerr_quiet( err, exit );
22869
22870 *ipaddrPtr = ipaddr;
22871 ipaddrPtr = &ipaddr->next;
22872 }
22873 }
22874 }
22875 }
22876 }
22877
22878 *outResults = (ServiceBrowserResults *) results;
22879 results = NULL;
22880 err = kNoErr;
22881
22882 exit:
22883 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
22884 return( err );
22885 }
22886
22887 //===========================================================================================================================
22888 // _SBDomainCreate
22889 //===========================================================================================================================
22890
22891 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
22892 {
22893 OSStatus err;
22894 SBDomain * obj;
22895
22896 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
22897 require_action( obj, exit, err = kNoMemoryErr );
22898
22899 obj->name = strdup( inName );
22900 require_action( obj->name, exit, err = kNoMemoryErr );
22901
22902 obj->browser = inBrowser;
22903
22904 *outDomain = obj;
22905 obj = NULL;
22906 err = kNoErr;
22907
22908 exit:
22909 if( obj ) _SBDomainFree( obj );
22910 return( err );
22911 }
22912
22913 //===========================================================================================================================
22914 // _SBDomainFree
22915 //===========================================================================================================================
22916
22917 static void _SBDomainFree( SBDomain *inDomain )
22918 {
22919 SBServiceType * type;
22920
22921 ForgetMem( &inDomain->name );
22922 DNSServiceForget( &inDomain->servicesQuery );
22923 while( ( type = inDomain->typeList ) != NULL )
22924 {
22925 inDomain->typeList = type->next;
22926 _SBServiceTypeFree( type );
22927 }
22928 free( inDomain );
22929 }
22930
22931 //===========================================================================================================================
22932 // _SBServiceTypeCreate
22933 //===========================================================================================================================
22934
22935 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
22936 {
22937 OSStatus err;
22938 SBServiceType * obj;
22939
22940 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
22941 require_action( obj, exit, err = kNoMemoryErr );
22942
22943 obj->name = strdup( inName );
22944 require_action( obj->name, exit, err = kNoMemoryErr );
22945
22946 *outType = obj;
22947 obj = NULL;
22948 err = kNoErr;
22949
22950 exit:
22951 if( obj ) _SBServiceTypeFree( obj );
22952 return( err );
22953 }
22954
22955 //===========================================================================================================================
22956 // _SBServiceTypeFree
22957 //===========================================================================================================================
22958
22959 static void _SBServiceTypeFree( SBServiceType *inType )
22960 {
22961 SBServiceBrowse * browse;
22962
22963 ForgetMem( &inType->name );
22964 while( ( browse = inType->browseList ) != NULL )
22965 {
22966 inType->browseList = browse->next;
22967 _SBServiceBrowseFree( browse );
22968 }
22969 free( inType );
22970 }
22971
22972 //===========================================================================================================================
22973 // _SBServiceBrowseCreate
22974 //===========================================================================================================================
22975
22976 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
22977 {
22978 OSStatus err;
22979 SBServiceBrowse * obj;
22980
22981 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
22982 require_action( obj, exit, err = kNoMemoryErr );
22983
22984 obj->ifIndex = inIfIndex;
22985 obj->browser = inBrowser;
22986 *outBrowse = obj;
22987 err = kNoErr;
22988
22989 exit:
22990 return( err );
22991 }
22992
22993 //===========================================================================================================================
22994 // _SBServiceBrowseFree
22995 //===========================================================================================================================
22996
22997 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
22998 {
22999 SBServiceInstance * instance;
23000
23001 DNSServiceForget( &inBrowse->browse );
23002 while( ( instance = inBrowse->instanceList ) != NULL )
23003 {
23004 inBrowse->instanceList = instance->next;
23005 _SBServiceInstanceFree( instance );
23006 }
23007 free( inBrowse );
23008 }
23009
23010 //===========================================================================================================================
23011 // _SBServiceInstanceCreate
23012 //===========================================================================================================================
23013
23014 static OSStatus
23015 _SBServiceInstanceCreate(
23016 const char * inName,
23017 const char * inType,
23018 const char * inDomain,
23019 uint32_t inIfIndex,
23020 uint64_t inDiscoverTimeUs,
23021 ServiceBrowserRef inBrowser,
23022 SBServiceInstance ** outInstance )
23023 {
23024 OSStatus err;
23025 SBServiceInstance * obj;
23026
23027 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
23028 require_action( obj, exit, err = kNoMemoryErr );
23029
23030 obj->name = strdup( inName );
23031 require_action( obj->name, exit, err = kNoMemoryErr );
23032
23033 ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
23034 require_action( obj->fqdn, exit, err = kNoMemoryErr );
23035
23036 obj->ifIndex = inIfIndex;
23037 obj->discoverTimeUs = inDiscoverTimeUs;
23038 obj->browser = inBrowser;
23039
23040 *outInstance = obj;
23041 obj = NULL;
23042 err = kNoErr;
23043
23044 exit:
23045 if( obj ) _SBServiceInstanceFree( obj );
23046 return( err );
23047 }
23048
23049 //===========================================================================================================================
23050 // _SBServiceInstanceFree
23051 //===========================================================================================================================
23052
23053 static void _SBServiceInstanceFree( SBServiceInstance *inInstance )
23054 {
23055 ForgetMem( &inInstance->name );
23056 ForgetMem( &inInstance->fqdn );
23057 DNSServiceForget( &inInstance->resolve );
23058 ForgetMem( &inInstance->hostname );
23059 ForgetMem( &inInstance->txtPtr );
23060 DNSServiceForget( &inInstance->getAddrInfo );
23061 ForgetSBIPAddressList( &inInstance->ipaddrList );
23062 free( inInstance );
23063 }
23064
23065 //===========================================================================================================================
23066 // _SBIPAddressCreate
23067 //===========================================================================================================================
23068
23069 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
23070 {
23071 OSStatus err;
23072 SBIPAddress * obj;
23073
23074 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
23075 require_action( obj, exit, err = kNoMemoryErr );
23076
23077 SockAddrCopy( inSockAddr, &obj->sip );
23078 obj->resolveTimeUs = inResolveTimeUs;
23079
23080 *outIPAddress = obj;
23081 err = kNoErr;
23082
23083 exit:
23084 return( err );
23085 }
23086
23087 //===========================================================================================================================
23088 // _SBIPAddressFree
23089 //===========================================================================================================================
23090
23091 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
23092 {
23093 free( inIPAddress );
23094 }
23095
23096 //===========================================================================================================================
23097 // _SBIPAddressFreeList
23098 //===========================================================================================================================
23099
23100 static void _SBIPAddressFreeList( SBIPAddress *inList )
23101 {
23102 SBIPAddress * ipaddr;
23103
23104 while( ( ipaddr = inList ) != NULL )
23105 {
23106 inList = ipaddr->next;
23107 _SBIPAddressFree( ipaddr );
23108 }
23109 }
23110
23111 //===========================================================================================================================
23112 // _SBRDomainCreate
23113 //===========================================================================================================================
23114
23115 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
23116 {
23117 OSStatus err;
23118 SBRDomain * obj;
23119
23120 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
23121 require_action( obj, exit, err = kNoMemoryErr );
23122
23123 obj->name = strdup( inName );
23124 require_action( obj->name, exit, err = kNoMemoryErr );
23125
23126 *outDomain = obj;
23127 obj = NULL;
23128 err = kNoErr;
23129
23130 exit:
23131 if( obj ) _SBRDomainFree( obj );
23132 return( err );
23133 }
23134
23135 //===========================================================================================================================
23136 // _SBRDomainFree
23137 //===========================================================================================================================
23138
23139 static void _SBRDomainFree( SBRDomain *inDomain )
23140 {
23141 SBRServiceType * type;
23142
23143 ForgetMem( &inDomain->name );
23144 while( ( type = inDomain->typeList ) != NULL )
23145 {
23146 inDomain->typeList = type->next;
23147 _SBRServiceTypeFree( type );
23148 }
23149 free( inDomain );
23150 }
23151
23152 //===========================================================================================================================
23153 // _SBRServiceTypeCreate
23154 //===========================================================================================================================
23155
23156 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
23157 {
23158 OSStatus err;
23159 SBRServiceType * obj;
23160
23161 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
23162 require_action( obj, exit, err = kNoMemoryErr );
23163
23164 obj->name = strdup( inName );
23165 require_action( obj->name, exit, err = kNoMemoryErr );
23166
23167 *outType = obj;
23168 obj = NULL;
23169 err = kNoErr;
23170
23171 exit:
23172 if( obj ) _SBRServiceTypeFree( obj );
23173 return( err );
23174 }
23175
23176 //===========================================================================================================================
23177 // _SBRServiceTypeFree
23178 //===========================================================================================================================
23179
23180 static void _SBRServiceTypeFree( SBRServiceType *inType )
23181 {
23182 SBRServiceInstance * instance;
23183
23184 ForgetMem( &inType->name );
23185 while( ( instance = inType->instanceList ) != NULL )
23186 {
23187 inType->instanceList = instance->next;
23188 _SBRServiceInstanceFree( instance );
23189 }
23190 free( inType );
23191 }
23192
23193 //===========================================================================================================================
23194 // _SBRServiceInstanceCreate
23195 //===========================================================================================================================
23196
23197 static OSStatus
23198 _SBRServiceInstanceCreate(
23199 const char * inName,
23200 uint32_t inInterfaceIndex,
23201 const char * inHostname,
23202 uint16_t inPort,
23203 const uint8_t * inTXTPtr,
23204 size_t inTXTLen,
23205 uint64_t inDiscoverTimeUs,
23206 uint64_t inResolveTimeUs,
23207 SBRServiceInstance ** outInstance )
23208 {
23209 OSStatus err;
23210 SBRServiceInstance * obj;
23211
23212 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
23213 require_action( obj, exit, err = kNoMemoryErr );
23214
23215 obj->name = strdup( inName );
23216 require_action( obj->name, exit, err = kNoMemoryErr );
23217
23218 if( inHostname )
23219 {
23220 obj->hostname = strdup( inHostname );
23221 require_action( obj->hostname, exit, err = kNoMemoryErr );
23222 }
23223 if( inTXTLen > 0 )
23224 {
23225 obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
23226 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
23227 obj->txtLen = inTXTLen;
23228 }
23229 obj->discoverTimeUs = inDiscoverTimeUs;
23230 obj->resolveTimeUs = inResolveTimeUs;
23231 obj->ifIndex = inInterfaceIndex;
23232 obj->port = inPort;
23233
23234 *outInstance = obj;
23235 obj = NULL;
23236 err = kNoErr;
23237
23238 exit:
23239 if( obj ) _SBRServiceInstanceFree( obj );
23240 return( err );
23241 }
23242
23243 //===========================================================================================================================
23244 // _SBRServiceInstanceFree
23245 //===========================================================================================================================
23246
23247 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
23248 {
23249 SBRIPAddress * ipaddr;
23250
23251 ForgetMem( &inInstance->name );
23252 ForgetMem( &inInstance->hostname );
23253 ForgetMem( &inInstance->txtPtr );
23254 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
23255 {
23256 inInstance->ipaddrList = ipaddr->next;
23257 _SBRIPAddressFree( ipaddr );
23258 }
23259 free( inInstance );
23260 }
23261
23262 //===========================================================================================================================
23263 // _SBRIPAddressCreate
23264 //===========================================================================================================================
23265
23266 static OSStatus
23267 _SBRIPAddressCreate(
23268 const struct sockaddr * inSockAddr,
23269 uint64_t inResolveTimeUs,
23270 SBRIPAddress ** outIPAddress )
23271 {
23272 OSStatus err;
23273 SBRIPAddress * obj;
23274
23275 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
23276 require_action( obj, exit, err = kNoMemoryErr );
23277
23278 SockAddrCopy( inSockAddr, &obj->sip );
23279 obj->resolveTimeUs = inResolveTimeUs;
23280
23281 *outIPAddress = obj;
23282 err = kNoErr;
23283
23284 exit:
23285 return( err );
23286 }
23287
23288 //===========================================================================================================================
23289 // _SBRIPAddressFree
23290 //===========================================================================================================================
23291
23292 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
23293 {
23294 free( inIPAddress );
23295 }
23296
23297 //===========================================================================================================================
23298 // _SocketWriteAll
23299 //
23300 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23301 //===========================================================================================================================
23302
23303 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
23304 {
23305 OSStatus err;
23306 const uint8_t * src;
23307 const uint8_t * end;
23308 fd_set writeSet;
23309 struct timeval timeout;
23310 ssize_t n;
23311
23312 FD_ZERO( &writeSet );
23313 src = (const uint8_t *) inData;
23314 end = src + inSize;
23315 while( src < end )
23316 {
23317 FD_SET( inSock, &writeSet );
23318 timeout.tv_sec = inTimeoutSecs;
23319 timeout.tv_usec = 0;
23320 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
23321 if( n == 0 ) { err = kTimeoutErr; goto exit; }
23322 err = map_socket_value_errno( inSock, n > 0, n );
23323 require_noerr( err, exit );
23324
23325 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
23326 err = map_socket_value_errno( inSock, n >= 0, n );
23327 if( err == EINTR ) continue;
23328 require_noerr( err, exit );
23329
23330 src += n;
23331 }
23332 err = kNoErr;
23333
23334 exit:
23335 return( err );
23336 }
23337
23338 //===========================================================================================================================
23339 // _ParseIPv4Address
23340 //
23341 // Warning: "inBuffer" may be modified even in error cases.
23342 //
23343 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23344 //===========================================================================================================================
23345
23346 static OSStatus _ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
23347 {
23348 OSStatus err;
23349 uint8_t * dst;
23350 int segments;
23351 int sawDigit;
23352 int c;
23353 int v;
23354
23355 check( inBuffer );
23356 check( outStr );
23357
23358 dst = inBuffer;
23359 *dst = 0;
23360 sawDigit = 0;
23361 segments = 0;
23362 for( ; ( c = *inStr ) != '\0'; ++inStr )
23363 {
23364 if( isdigit_safe( c ) )
23365 {
23366 v = ( *dst * 10 ) + ( c - '0' );
23367 require_action_quiet( v <= 255, exit, err = kRangeErr );
23368 *dst = (uint8_t) v;
23369 if( !sawDigit )
23370 {
23371 ++segments;
23372 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
23373 sawDigit = 1;
23374 }
23375 }
23376 else if( ( c == '.' ) && sawDigit )
23377 {
23378 require_action_quiet( segments < 4, exit, err = kMalformedErr );
23379 *++dst = 0;
23380 sawDigit = 0;
23381 }
23382 else
23383 {
23384 break;
23385 }
23386 }
23387 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
23388
23389 *outStr = inStr;
23390 err = kNoErr;
23391
23392 exit:
23393 return( err );
23394 }
23395
23396 //===========================================================================================================================
23397 // _StringToIPv4Address
23398 //
23399 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23400 //===========================================================================================================================
23401
23402 static OSStatus
23403 _StringToIPv4Address(
23404 const char * inStr,
23405 StringToIPAddressFlags inFlags,
23406 uint32_t * outIP,
23407 int * outPort,
23408 uint32_t * outSubnet,
23409 uint32_t * outRouter,
23410 const char ** outStr )
23411 {
23412 OSStatus err;
23413 uint8_t buf[ 4 ];
23414 int c;
23415 uint32_t ip;
23416 int hasPort;
23417 int port;
23418 int hasPrefix;
23419 int prefix;
23420 uint32_t subnetMask;
23421 uint32_t router;
23422
23423 require_action( inStr, exit, err = kParamErr );
23424
23425 // Parse the address-only part of the address (e.g. "1.2.3.4").
23426
23427 err = _ParseIPv4Address( inStr, buf, &inStr );
23428 require_noerr_quiet( err, exit );
23429 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
23430 c = *inStr;
23431
23432 // Parse the port (if any).
23433
23434 hasPort = 0;
23435 port = 0;
23436 if( c == ':' )
23437 {
23438 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23439 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23440 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23441 hasPort = 1;
23442 }
23443
23444 // Parse the prefix length (if any).
23445
23446 hasPrefix = 0;
23447 prefix = 0;
23448 subnetMask = 0;
23449 router = 0;
23450 if( c == '/' )
23451 {
23452 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23453 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23454 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
23455 hasPrefix = 1;
23456
23457 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
23458 router = ( ip & subnetMask ) | 1;
23459 }
23460
23461 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23462
23463 if( outIP ) *outIP = ip;
23464 if( outPort && hasPort ) *outPort = port;
23465 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
23466 if( outRouter && hasPrefix ) *outRouter = router;
23467 if( outStr ) *outStr = inStr;
23468 err = kNoErr;
23469
23470 exit:
23471 return( err );
23472 }
23473
23474 //===========================================================================================================================
23475 // _ParseIPv6Address
23476 //
23477 // Note: Parsed according to the rules specified in RFC 3513.
23478 // Warning: "inBuffer" may be modified even in error cases.
23479 //
23480 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23481 //===========================================================================================================================
23482
23483 static OSStatus _ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
23484 {
23485 // Table to map uppercase hex characters - '0' to their numeric values.
23486 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23487 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 };
23488 OSStatus err;
23489 const char * ptr;
23490 uint8_t * dst;
23491 uint8_t * lim;
23492 uint8_t * colonPtr;
23493 int c;
23494 int sawDigit;
23495 unsigned int v;
23496 int i;
23497 int n;
23498
23499 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23500
23501 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
23502
23503 // Special case leading :: (e.g. "::1") to simplify processing later.
23504
23505 if( *inStr == ':' )
23506 {
23507 ++inStr;
23508 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
23509 }
23510
23511 // Parse the address.
23512
23513 ptr = inStr;
23514 dst = inBuffer;
23515 lim = dst + 16;
23516 colonPtr = NULL;
23517 sawDigit = 0;
23518 v = 0;
23519 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
23520 {
23521 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
23522 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
23523 {
23524 c -= '0';
23525 check( c < (int) countof( kASCIItoHexTable ) );
23526 v = ( v << 4 ) | kASCIItoHexTable[ c ];
23527 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
23528 sawDigit = 1;
23529 continue;
23530 }
23531 if( c == ':' )
23532 {
23533 ptr = inStr;
23534 if( !sawDigit )
23535 {
23536 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
23537 colonPtr = dst;
23538 continue;
23539 }
23540 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
23541 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23542 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23543 *dst++ = (uint8_t)( v & 0xFF );
23544 sawDigit = 0;
23545 v = 0;
23546 continue;
23547 }
23548
23549 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23550
23551 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
23552 {
23553 err = _ParseIPv4Address( ptr, dst, &inStr );
23554 require_noerr_quiet( err, exit );
23555 dst += 4;
23556 sawDigit = 0;
23557 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
23558 }
23559 break;
23560 }
23561 if( sawDigit )
23562 {
23563 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
23564 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
23565 *dst++ = (uint8_t)( v & 0xFF );
23566 }
23567 check( dst <= lim );
23568 if( colonPtr )
23569 {
23570 require_action_quiet( dst < lim, exit, err = kOverrunErr );
23571 n = (int)( dst - colonPtr );
23572 for( i = 1; i <= n; ++i )
23573 {
23574 lim[ -i ] = colonPtr[ n - i ];
23575 colonPtr[ n - i ] = 0;
23576 }
23577 dst = lim;
23578 }
23579 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
23580
23581 *outStr = inStr - 1;
23582 err = kNoErr;
23583
23584 exit:
23585 return( err );
23586 }
23587
23588 //===========================================================================================================================
23589 // _ParseIPv6Scope
23590 //
23591 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23592 //===========================================================================================================================
23593
23594 static OSStatus _ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
23595 {
23596 #if( TARGET_OS_POSIX )
23597 OSStatus err;
23598 char scopeStr[ 64 ];
23599 char * dst;
23600 char * lim;
23601 int c;
23602 uint32_t scope;
23603 const char * ptr;
23604
23605 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23606
23607 dst = scopeStr;
23608 lim = dst + ( countof( scopeStr ) - 1 );
23609 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
23610 {
23611 *dst++ = *inStr++;
23612 }
23613 *dst = '\0';
23614 check( dst <= lim );
23615
23616 // First try to map as a name and if that fails, treat it as a numeric scope.
23617
23618 scope = if_nametoindex( scopeStr );
23619 if( scope == 0 )
23620 {
23621 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
23622 {
23623 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
23624 }
23625 require_action_quiet( c == '\0', exit, err = kMalformedErr );
23626 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
23627 }
23628
23629 *outScope = scope;
23630 *outStr = inStr;
23631 err = kNoErr;
23632
23633 exit:
23634 return( err );
23635 #else
23636 OSStatus err;
23637 uint32_t scope;
23638 const char * start;
23639 int c;
23640
23641 scope = 0;
23642 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
23643 {
23644 scope = ( scope * 10 ) + ( c - '0' );
23645 }
23646 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
23647
23648 *outScope = scope;
23649 *outStr = inStr;
23650 err = kNoErr;
23651
23652 exit:
23653 return( err );
23654 #endif
23655 }
23656
23657 //===========================================================================================================================
23658 // _StringToIPv6Address
23659 //
23660 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23661 //===========================================================================================================================
23662
23663 static OSStatus
23664 _StringToIPv6Address(
23665 const char * inStr,
23666 StringToIPAddressFlags inFlags,
23667 uint8_t outIPv6[ 16 ],
23668 uint32_t * outScope,
23669 int * outPort,
23670 int * outPrefix,
23671 const char ** outStr )
23672 {
23673 OSStatus err;
23674 uint8_t ipv6[ 16 ];
23675 int c;
23676 int hasScope;
23677 uint32_t scope;
23678 int hasPort;
23679 int port;
23680 int hasPrefix;
23681 int prefix;
23682 int hasBracket;
23683 int i;
23684
23685 require_action( inStr, exit, err = kParamErr );
23686
23687 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23688
23689 // Parse the address-only part of the address (e.g. "1::1").
23690
23691 err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
23692 require_noerr_quiet( err, exit );
23693 c = *inStr;
23694
23695 // Parse the scope, port, or prefix length.
23696
23697 hasScope = 0;
23698 scope = 0;
23699 hasPort = 0;
23700 port = 0;
23701 hasPrefix = 0;
23702 prefix = 0;
23703 hasBracket = 0;
23704 for( ;; )
23705 {
23706 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
23707 {
23708 require_action_quiet( !hasScope, exit, err = kMalformedErr );
23709 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
23710 ++inStr;
23711 err = _ParseIPv6Scope( inStr, &scope, &inStr );
23712 require_noerr_quiet( err, exit );
23713 hasScope = 1;
23714 c = *inStr;
23715 }
23716 else if( c == ':' ) // Port (e.g. ":80")
23717 {
23718 require_action_quiet( !hasPort, exit, err = kMalformedErr );
23719 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
23720 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
23721 require_action_quiet( port <= 65535, exit, err = kRangeErr );
23722 hasPort = 1;
23723 }
23724 else if( c == '/' ) // Prefix Length (e.g. "/64")
23725 {
23726 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
23727 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
23728 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
23729 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
23730 hasPrefix = 1;
23731 }
23732 else if( c == ']' )
23733 {
23734 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
23735 hasBracket = 1;
23736 c = *( ++inStr );
23737 }
23738 else
23739 {
23740 break;
23741 }
23742 }
23743
23744 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23745
23746 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
23747 if( outScope && hasScope ) *outScope = scope;
23748 if( outPort && hasPort ) *outPort = port;
23749 if( outPrefix && hasPrefix ) *outPrefix = prefix;
23750 if( outStr ) *outStr = inStr;
23751 err = kNoErr;
23752
23753 exit:
23754 return( err );
23755 }
23756
23757 //===========================================================================================================================
23758 // _StringArray_Free
23759 //
23760 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23761 //===========================================================================================================================
23762
23763 static void _StringArray_Free( char **inArray, size_t inCount )
23764 {
23765 size_t i;
23766
23767 for( i = 0; i < inCount; ++i )
23768 {
23769 free( inArray[ i ] );
23770 }
23771 if( inCount > 0 ) free( inArray );
23772 }
23773
23774 //===========================================================================================================================
23775 // _ParseQuotedEscapedString
23776 //
23777 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23778 //===========================================================================================================================
23779
23780 static Boolean
23781 _ParseQuotedEscapedString(
23782 const char * inSrc,
23783 const char * inEnd,
23784 const char * inDelimiters,
23785 char * inBuf,
23786 size_t inMaxLen,
23787 size_t * outCopiedLen,
23788 size_t * outTotalLen,
23789 const char ** outSrc )
23790 {
23791 const unsigned char * src;
23792 const unsigned char * end;
23793 unsigned char * dst;
23794 unsigned char * lim;
23795 unsigned char c;
23796 unsigned char c2;
23797 size_t totalLen;
23798 Boolean singleQuote;
23799 Boolean doubleQuote;
23800
23801 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
23802 src = (const unsigned char *) inSrc;
23803 end = (const unsigned char *) inEnd;
23804 dst = (unsigned char *) inBuf;
23805 lim = dst + inMaxLen;
23806 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
23807 if( src >= end ) return( false );
23808
23809 // Parse each argument from the string.
23810 //
23811 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23812
23813 totalLen = 0;
23814 singleQuote = false;
23815 doubleQuote = false;
23816 while( src < end )
23817 {
23818 c = *src++;
23819 if( singleQuote )
23820 {
23821 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23822
23823 if( c == '\'' )
23824 {
23825 singleQuote = false;
23826 continue;
23827 }
23828 }
23829 else if( doubleQuote )
23830 {
23831 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23832 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23833 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23834 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23835 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23836
23837 if( c == '"' )
23838 {
23839 doubleQuote = false;
23840 continue;
23841 }
23842 else if( c == '\\' )
23843 {
23844 if( src < end )
23845 {
23846 c2 = *src;
23847 if( ( c2 == '"' ) || ( c2 == '\\' ) )
23848 {
23849 ++src;
23850 c = c2;
23851 }
23852 else if( c2 == '\n' )
23853 {
23854 ++src;
23855 continue;
23856 }
23857 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
23858 {
23859 ++src;
23860 c = c2;
23861 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23862 {
23863 c = HexPairToByte( src );
23864 src += 2;
23865 }
23866 }
23867 else if( isoctal_safe( c2 ) )
23868 {
23869 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23870 {
23871 c = OctalTripleToByte( src );
23872 src += 3;
23873 }
23874 }
23875 }
23876 }
23877 }
23878 else if( strchr( inDelimiters, c ) )
23879 {
23880 break;
23881 }
23882 else if( c == '\\' )
23883 {
23884 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23885 // A backslash followed by a newline disappears completely.
23886 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23887 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23888
23889 if( src < end )
23890 {
23891 c = *src;
23892 if( c == '\n' )
23893 {
23894 ++src;
23895 continue;
23896 }
23897 else if( ( c == 'x' ) || ( c == 'X' ) )
23898 {
23899 ++src;
23900 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
23901 {
23902 c = HexPairToByte( src );
23903 src += 2;
23904 }
23905 }
23906 else if( isoctal_safe( c ) )
23907 {
23908 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
23909 {
23910 c = OctalTripleToByte( src );
23911 src += 3;
23912 }
23913 else
23914 {
23915 ++src;
23916 }
23917 }
23918 else
23919 {
23920 ++src;
23921 }
23922 }
23923 }
23924 else if( c == '\'' )
23925 {
23926 singleQuote = true;
23927 continue;
23928 }
23929 else if( c == '"' )
23930 {
23931 doubleQuote = true;
23932 continue;
23933 }
23934
23935 if( dst < lim )
23936 {
23937 if( inBuf ) *dst = c;
23938 ++dst;
23939 }
23940 ++totalLen;
23941 }
23942
23943 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
23944 if( outTotalLen ) *outTotalLen = totalLen;
23945 if( outSrc ) *outSrc = (const char *) src;
23946 return( true );
23947 }
23948
23949 //===========================================================================================================================
23950 // _ServerSocketOpenEx2
23951 //
23952 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23953 //===========================================================================================================================
23954
23955 static OSStatus
23956 _ServerSocketOpenEx2(
23957 int inFamily,
23958 int inType,
23959 int inProtocol,
23960 const void * inAddr,
23961 int inPort,
23962 int * outPort,
23963 int inRcvBufSize,
23964 Boolean inNoPortReuse,
23965 SocketRef * outSock )
23966 {
23967 OSStatus err;
23968 int port;
23969 SocketRef sock;
23970 int name;
23971 int option;
23972 sockaddr_ip sip;
23973 socklen_t len;
23974
23975 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
23976
23977 sock = socket( inFamily, inType, inProtocol );
23978 err = map_socket_creation_errno( sock );
23979 require_noerr_quiet( err, exit );
23980
23981 #if( defined( SO_NOSIGPIPE ) )
23982 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
23983 #endif
23984
23985 err = SocketMakeNonBlocking( sock );
23986 require_noerr( err, exit );
23987
23988 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23989 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23990 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23991 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23992
23993 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
23994 check_noerr( err );
23995
23996 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23997
23998 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
23999 {
24000 option = 1;
24001 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
24002 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
24003 err = map_socket_noerr_errno( sock, err );
24004 require_noerr( err, exit );
24005 }
24006
24007 if( inFamily == AF_INET )
24008 {
24009 // Bind to the port. If it fails, retry with a dynamic port.
24010
24011 memset( &sip.v4, 0, sizeof( sip.v4 ) );
24012 SIN_LEN_SET( &sip.v4 );
24013 sip.v4.sin_family = AF_INET;
24014 sip.v4.sin_port = htons( (uint16_t) port );
24015 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
24016 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
24017 err = map_socket_noerr_errno( sock, err );
24018 if( err && ( inPort < 0 ) )
24019 {
24020 sip.v4.sin_port = 0;
24021 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
24022 err = map_socket_noerr_errno( sock, err );
24023 }
24024 require_noerr( err, exit );
24025 }
24026 #if( defined( AF_INET6 ) )
24027 else if( inFamily == AF_INET6 )
24028 {
24029 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
24030
24031 option = 1;
24032 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
24033 err = map_socket_noerr_errno( sock, err );
24034 require_noerr( err, exit );
24035
24036 // Bind to the port. If it fails, retry with a dynamic port.
24037
24038 memset( &sip.v6, 0, sizeof( sip.v6 ) );
24039 SIN6_LEN_SET( &sip.v6 );
24040 sip.v6.sin6_family = AF_INET6;
24041 sip.v6.sin6_port = htons( (uint16_t) port );
24042 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
24043 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24044 err = map_socket_noerr_errno( sock, err );
24045 if( err && ( inPort < 0 ) )
24046 {
24047 sip.v6.sin6_port = 0;
24048 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
24049 err = map_socket_noerr_errno( sock, err );
24050 }
24051 require_noerr( err, exit );
24052 }
24053 #endif
24054 else
24055 {
24056 dlogassert( "Unsupported family: %d", inFamily );
24057 err = kUnsupportedErr;
24058 goto exit;
24059 }
24060
24061 if( inType == SOCK_STREAM )
24062 {
24063 err = listen( sock, SOMAXCONN );
24064 err = map_socket_noerr_errno( sock, err );
24065 if( err )
24066 {
24067 err = listen( sock, 5 );
24068 err = map_socket_noerr_errno( sock, err );
24069 require_noerr( err, exit );
24070 }
24071 }
24072
24073 if( outPort )
24074 {
24075 len = (socklen_t) sizeof( sip );
24076 err = getsockname( sock, &sip.sa, &len );
24077 err = map_socket_noerr_errno( sock, err );
24078 require_noerr( err, exit );
24079
24080 *outPort = SockAddrGetPort( &sip );
24081 }
24082 *outSock = sock;
24083 sock = kInvalidSocketRef;
24084
24085 exit:
24086 ForgetSocket( &sock );
24087 return( err );
24088 }
24089
24090 //===========================================================================================================================
24091 // _memdup
24092 //
24093 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24094 //===========================================================================================================================
24095
24096 static void * _memdup( const void *inPtr, size_t inLen )
24097 {
24098 void * mem;
24099
24100 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24101 require( mem, exit );
24102 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
24103
24104 exit:
24105 return( mem );
24106 }
24107
24108 //===========================================================================================================================
24109 // _memicmp
24110 //
24111 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24112 //===========================================================================================================================
24113
24114 static int _memicmp( const void *inP1, const void *inP2, size_t inLen )
24115 {
24116 const unsigned char * p1;
24117 const unsigned char * e1;
24118 const unsigned char * p2;
24119 int c1;
24120 int c2;
24121
24122 p1 = (const unsigned char *) inP1;
24123 e1 = p1 + inLen;
24124 p2 = (const unsigned char *) inP2;
24125 while( p1 < e1 )
24126 {
24127 c1 = *p1++;
24128 c2 = *p2++;
24129 c1 = tolower( c1 );
24130 c2 = tolower( c2 );
24131 if( c1 < c2 ) return( -1 );
24132 if( c1 > c2 ) return( 1 );
24133 }
24134 return( 0 );
24135 }
24136
24137 //===========================================================================================================================
24138 // _FNV1
24139 //
24140 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24141 //===========================================================================================================================
24142
24143 static uint32_t _FNV1( const void *inData, size_t inSize )
24144 {
24145 const uint8_t * src = (const uint8_t *) inData;
24146 const uint8_t * const end = src + inSize;
24147 uint32_t hash;
24148
24149 hash = 0x811c9dc5U;
24150 while( src != end )
24151 {
24152 hash *= 0x01000193;
24153 hash ^= *src++;
24154 }
24155 return( hash );
24156 }