]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dnssdutil.c
mDNSResponder-1310.60.4.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / dnssdutil.c
1 /*
2 * Copyright (c) 2016-2020 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "DNSMessage.h"
18 #include "DNSServerDNSSEC.h"
19
20 #include <CoreUtils/CoreUtils.h>
21 #include <dns_sd.h>
22 #include <dns_sd_private.h>
23 #include <pcap.h>
24
25 #include CF_RUNTIME_HEADER
26
27 #if( TARGET_OS_DARWIN )
28 #include <CFNetwork/CFHost.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <SystemConfiguration/SCPrivate.h>
31 #include <dnsinfo.h>
32 #include <libproc.h>
33 #include <netdb.h>
34 #include <netinet6/in6_var.h>
35 #include <netinet6/nd6.h>
36 #include <spawn.h>
37 #include <sys/proc_info.h>
38 #include <xpc/xpc.h>
39 #endif
40
41 #if( TARGET_OS_POSIX )
42 #include <sys/resource.h>
43 #include <spawn.h>
44 #endif
45
46 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
47 #define DNSSDUTIL_INCLUDE_DNSCRYPT 0
48 #endif
49
50 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
51 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
52 #endif
53
54 #if( !defined( MDNSRESPONDER_PROJECT ) )
55 #define MDNSRESPONDER_PROJECT 0
56 #endif
57
58 #if( MDNSRESPONDER_PROJECT )
59 #include <CoreFoundation/CFXPCBridge.h>
60 #include <dns_services.h>
61 #include "dnssd_private.h"
62 #include "mdns_private.h"
63 #include "TestUtils.h"
64 // Set ENABLE_DNSSDUTIL_DNSSEC_TEST to 1 to enable DNSSEC test functionality.
65 #define ENABLE_DNSSDUTIL_DNSSEC_TEST 0
66 #endif
67
68 //===========================================================================================================================
69 // Versioning
70 //===========================================================================================================================
71
72 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
73
74 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
75 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
76 #endif
77
78 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
79
80 //===========================================================================================================================
81 // DNS-SD
82 //===========================================================================================================================
83
84 // DNS-SD API flag descriptors
85
86 #define kDNSServiceFlagsDescriptors \
87 "\x00" "AutoTrigger\0" \
88 "\x01" "Add\0" \
89 "\x02" "Default\0" \
90 "\x03" "NoAutoRename\0" \
91 "\x04" "Shared\0" \
92 "\x05" "Unique\0" \
93 "\x06" "BrowseDomains\0" \
94 "\x07" "RegistrationDomains\0" \
95 "\x08" "LongLivedQuery\0" \
96 "\x09" "AllowRemoteQuery\0" \
97 "\x0A" "ForceMulticast\0" \
98 "\x0B" "KnownUnique\0" \
99 "\x0C" "ReturnIntermediates\0" \
100 "\x0D" "DenyConstrained\0" \
101 "\x0E" "ShareConnection\0" \
102 "\x0F" "SuppressUnusable\0" \
103 "\x10" "Timeout\0" \
104 "\x11" "IncludeP2P\0" \
105 "\x12" "WakeOnResolve\0" \
106 "\x13" "BackgroundTrafficClass\0" \
107 "\x14" "IncludeAWDL\0" \
108 "\x15" "EnableDNSSEC\0" \
109 "\x16" "UnicastResponse\0" \
110 "\x17" "ValidateOptional\0" \
111 "\x18" "WakeOnlyService\0" \
112 "\x19" "ThresholdOne\0" \
113 "\x1A" "ThresholdFinder\0" \
114 "\x1B" "DenyCellular\0" \
115 "\x1C" "ServiceIndex\0" \
116 "\x1D" "DenyExpensive\0" \
117 "\x1E" "PathEvaluationDone\0" \
118 "\x1F" "AllowExpiredAnswers\0" \
119 "\x00"
120
121 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
122
123 #define kDNSServiceProtocolDescriptors \
124 "\x00" "IPv4\0" \
125 "\x01" "IPv6\0" \
126 "\x04" "UDP\0" \
127 "\x05" "TCP\0" \
128 "\x00"
129
130 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
131
132 //===========================================================================================================================
133 // DNS
134 //===========================================================================================================================
135
136 #define kDNSPort 53
137 #define kDNSMaxUDPMessageSize 512
138 #define kDNSMaxTCPMessageSize UINT16_MAX
139
140 #define kDNSRecordDataLengthMax UINT16_MAX
141
142 //===========================================================================================================================
143 // mDNS
144 //===========================================================================================================================
145
146 #define kMDNSPort 5353
147
148 #define kDefaultMDNSMessageID 0
149 #define kDefaultMDNSQueryFlags 0
150
151 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
152
153 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
154 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
155
156 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
157
158 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
159
160 #define kLocalStr "\x05" "local"
161 #define kLocalLabel ( (const uint8_t *) kLocalStr )
162 #define kLocalName ( (const uint8_t *) kLocalStr )
163 #define kLocalNameLen sizeof( kLocalStr )
164
165 //===========================================================================================================================
166 // Test Address Blocks
167 //===========================================================================================================================
168
169 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
170
171 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
172
173 #define kDNSServerReverseIPv4DomainStr "113.0.203.in-addr.arpa."
174 #define kDNSServerReverseIPv4DomainName \
175 ( (const uint8_t *) "\x3" "113" "\x1" "0" "\x3" "203" "\x7" "in-addr" "\x4" "arpa" )
176
177 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
178
179 static const uint8_t kDNSServerBaseAddrV6[] =
180 {
181 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/96
182 };
183 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
184
185 #define kDNSServerReverseIPv6DomainStr "0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."
186 #define kDNSServerReverseIPv6DomainName \
187 ( (const uint8_t *) "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" \
188 "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" \
189 "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "1" "\x1" "0" "\x1" "0" "\x1" "0" \
190 "\x1" "8" "\x1" "b" "\x1" "d" "\x1" "0" "\x1" "1" "\x1" "0" "\x1" "0" "\x1" "2" \
191 "\x3" "ip6" "\x4" "arpa" )
192
193 static const uint8_t kMDNSReplierBaseAddrV6[] =
194 {
195 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
196 };
197 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
198
199 static const uint8_t kMDNSReplierLinkLocalBaseAddrV6[] =
200 {
201 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // fe80::/96
202 };
203 check_compile_time( sizeof( kMDNSReplierLinkLocalBaseAddrV6 ) == 16 );
204
205 // Bad IPv4 and IPv6 Address Blocks
206 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
207 // in 203.0.113.0/24 nor 2001:db8:1::/120.
208
209 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
210
211 static const uint8_t kDNSServerBadBaseAddrV6[] =
212 {
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
214 };
215 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
216
217 #if( TARGET_OS_DARWIN )
218 // IPv6 Unique Local Address for assigning extra randomly-generated IPv6 addresses to the loopback interface.
219 // 40-bit Global ID: 0xEDF03555E4 (randomly-generated)
220 // 16-bit Subnet ID: 0
221 // See <https://tools.ietf.org/html/rfc4193#section-3.1>.
222
223 static const uint8_t kExtraLoopbackIPv6Prefix[] =
224 {
225 0xFD, 0xED, 0xF0, 0x35, 0x55, 0xE4, 0x00, 0x00 // fded:f035:55e4::/64
226 };
227
228 #define kExtraLoopbackIPv6PrefixBitLen 64
229 check_compile_time( ( sizeof( kExtraLoopbackIPv6Prefix ) * 8 ) == kExtraLoopbackIPv6PrefixBitLen );
230 #endif
231
232 //===========================================================================================================================
233 // DNS Server Domains
234 //===========================================================================================================================
235
236 #define kDNSServerDomain_Default ( (const uint8_t *) "\x01" "d" "\x04" "test" )
237 #define kDNSServerDomain_DNSSEC ( (const uint8_t *) "\x06" "dnssec" "\x04" "test" )
238
239 //===========================================================================================================================
240 // Misc.
241 //===========================================================================================================================
242
243 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
244 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
245
246 #if( !defined( kWhiteSpaceCharSet ) )
247 #define kWhiteSpaceCharSet "\t\n\v\f\r "
248 #endif
249
250 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
251 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
252
253 #define kNoSuchRecordStr "No Such Record"
254 #define kNoSuchRecordAStr "No Such Record (A)"
255 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
256 #define kNoSuchNameStr "No Such Name"
257
258 #define kRootLabel ( (const uint8_t *) "" )
259
260 #if !defined( nw_forget )
261 #define nw_forget( X ) ForgetCustom( X, nw_release )
262 #endif
263
264 //===========================================================================================================================
265 // Gerneral Command Options
266 //===========================================================================================================================
267
268 // Command option macros
269
270 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
271 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
272 (SHORT_HELP), NULL )
273
274 #define kRequiredOptionSuffix " [REQUIRED]"
275
276 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
277 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
278 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
279 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
280
281 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
282 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
283
284 #define IntegerOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
285 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
286 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
287 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
288
289 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
290 IntegerOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
291
292 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
293 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
294 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
295 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
296
297 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
298 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
299
300 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
301 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
302 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
303 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
304
305 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
306 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
307
308 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
309 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
310 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
311 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
312
313 // DNS-SD API flag options
314
315 static int gDNSSDFlags = 0;
316 static int gDNSSDFlag_AllowExpiredAnswers = false;
317 static int gDNSSDFlag_BrowseDomains = false;
318 static int gDNSSDFlag_DenyCellular = false;
319 static int gDNSSDFlag_DenyConstrained = false;
320 static int gDNSSDFlag_DenyExpensive = false;
321 static int gDNSSDFlag_ForceMulticast = false;
322 static int gDNSSDFlag_IncludeAWDL = false;
323 static int gDNSSDFlag_KnownUnique = false;
324 static int gDNSSDFlag_NoAutoRename = false;
325 static int gDNSSDFlag_PathEvaluationDone = false;
326 static int gDNSSDFlag_RegistrationDomains = false;
327 static int gDNSSDFlag_ReturnIntermediates = false;
328 static int gDNSSDFlag_Shared = false;
329 static int gDNSSDFlag_SuppressUnusable = false;
330 static int gDNSSDFlag_Timeout = false;
331 static int gDNSSDFlag_UnicastResponse = false;
332 static int gDNSSDFlag_Unique = false;
333 static int gDNSSDFlag_WakeOnResolve = false;
334 static int gDNSSDFlag_EnableDNSSEC = false;
335
336 #define DNSSDFlagsOption() \
337 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
338 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
339
340 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
341 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
342
343 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
344 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
345 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
346 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
347 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
348 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
349 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
350 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
351 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
352 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
353 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
354 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
355 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
356 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
357 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
358 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
359 #define DNSSDFlagsOption_EnableDNSSEC() DNSSDFlagOption( 'D', EnableDNSSEC )
360
361 // Interface option
362
363 static const char * gInterface = NULL;
364
365 #define InterfaceOption() \
366 StringOption( 'i', "interface", &gInterface, "interface", \
367 "Network interface by name or index. Use index -1 for local-only.", false )
368
369 // Connection options
370
371 #define kConnectionArg_Normal ""
372 #define kConnectionArgPrefix_PID "pid:"
373 #define kConnectionArgPrefix_UUID "uuid:"
374
375 static const char * gConnectionOpt = kConnectionArg_Normal;
376
377 #define ConnectionOptions() \
378 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
379 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
380 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
381
382 #define kConnectionSection_Name "Connection Option"
383 #define kConnectionSection_Text \
384 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
385 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
386 "specifying the connection option without an argument, i.e.,\n" \
387 "\n" \
388 " --connection\n" \
389 "\n" \
390 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
391 "\n" \
392 " --connection=pid:<PID>\n" \
393 "\n" \
394 "to specify the delegator by PID, or use\n" \
395 "\n" \
396 " --connection=uuid:<UUID>\n" \
397 "\n" \
398 "to specify the delegator by UUID.\n" \
399 "\n" \
400 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
401 "\n" \
402 " --no-connection\n"
403
404 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
405
406 // Help text for record data options
407
408 #define kRDataArgPrefix_Domain "domain:"
409 #define kRDataArgPrefix_File "file:"
410 #define kRDataArgPrefix_HexString "hex:"
411 #define kRDataArgPrefix_IPv4 "ipv4:"
412 #define kRDataArgPrefix_IPv6 "ipv6:"
413 #define kRDataArgPrefix_SRV "srv:"
414 #define kRDataArgPrefix_String "string:"
415 #define kRDataArgPrefix_TXT "txt:"
416
417 #define kRecordDataSection_Name "Record Data Arguments"
418 #define kRecordDataSection_Text \
419 "A record data argument is specified in one of the following formats:\n" \
420 "\n" \
421 "Format Syntax Example\n" \
422 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
423 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
424 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
425 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
426 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
427 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
428 "String string:<string> string:'\\x09color=red'\n" \
429 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
430 "\n" \
431 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
432
433 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
434
435 // Fallback DNS service option
436
437 #if( MDNSRESPONDER_PROJECT )
438 static const char * gFallbackDNSService = NULL;
439
440 #define kFallbackDNSServiceArgPrefix_DoH "doh:"
441 #define kFallbackDNSServiceArgPrefix_DoT "dot:"
442
443 #define FallbackDNSServiceGroup() CLI_OPTION_GROUP( "Default Fallback DNS Service" )
444 #define FallbackDNSServiceOption() \
445 StringOptionEx( 0, "fallback", &gFallbackDNSService, "DNS service", "Default fallback DNS service to set.", false, \
446 "\n" \
447 "When this option is used, an nw_resolver_config is created for the specified DoH or DoT service.\n" \
448 "DNSServiceSetResolverDefaults() is then used to set the DNS service described by nw_resolver_config as the\n" \
449 "default fallback DNS service for the dnssdutil process.\n" \
450 "\n" \
451 "To specify a DNS over HTTPS (DoH) service, use\n" \
452 "\n" \
453 " --fallback=doh:<URL>\n" \
454 "\n" \
455 "Example: --fallback=doh:https://dns.example.com/dns-query\n" \
456 "\n" \
457 "To specify a DNS over TLS (DoT) service, use\n" \
458 "\n" \
459 " --fallback=dot:<hostname>\n" \
460 "\n" \
461 "Example: --fallback=dot:dns.example.com\n" \
462 )
463 #endif
464
465 //===========================================================================================================================
466 // Output Formatting
467 //===========================================================================================================================
468
469 #define kOutputFormatStr_JSON "json"
470 #define kOutputFormatStr_XML "xml"
471 #define kOutputFormatStr_Binary "binary"
472
473 typedef enum
474 {
475 kOutputFormatType_Invalid = 0,
476 kOutputFormatType_JSON = 1,
477 kOutputFormatType_XML = 2,
478 kOutputFormatType_Binary = 3
479
480 } OutputFormatType;
481
482 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
483 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
484 "\n" \
485 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
486 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
487 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
488 "\n" \
489 )
490
491 //===========================================================================================================================
492 // Browse Command Options
493 //===========================================================================================================================
494
495 static char ** gBrowse_ServiceTypes = NULL;
496 static size_t gBrowse_ServiceTypesCount = 0;
497 static const char * gBrowse_Domain = NULL;
498 static int gBrowse_DoResolve = false;
499 static int gBrowse_QueryTXT = false;
500 static int gBrowse_TimeLimitSecs = 0;
501
502 static CLIOption kBrowseOpts[] =
503 {
504 InterfaceOption(),
505 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
506 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
507
508 CLI_OPTION_GROUP( "Flags" ),
509 DNSSDFlagsOption(),
510 DNSSDFlagsOption_IncludeAWDL(),
511
512 CLI_OPTION_GROUP( "Operation" ),
513 ConnectionOptions(),
514 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
515 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
516 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
517
518 ConnectionSection(),
519 CLI_OPTION_END()
520 };
521
522 //===========================================================================================================================
523 // GetAddrInfo Command Options
524 //===========================================================================================================================
525
526 static const char * gGetAddrInfo_Name = NULL;
527 static int gGetAddrInfo_ProtocolIPv4 = false;
528 static int gGetAddrInfo_ProtocolIPv6 = false;
529 static int gGetAddrInfo_OneShot = false;
530 static int gGetAddrInfo_TimeLimitSecs = 0;
531
532 static CLIOption kGetAddrInfoOpts[] =
533 {
534 InterfaceOption(),
535 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
536 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
537 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
538
539 CLI_OPTION_GROUP( "Flags" ),
540 DNSSDFlagsOption(),
541 DNSSDFlagsOption_AllowExpiredAnswers(),
542 DNSSDFlagsOption_DenyCellular(),
543 DNSSDFlagsOption_DenyConstrained(),
544 DNSSDFlagsOption_DenyExpensive(),
545 DNSSDFlagsOption_IncludeAWDL(),
546 DNSSDFlagsOption_PathEvalDone(),
547 DNSSDFlagsOption_ReturnIntermediates(),
548 DNSSDFlagsOption_SuppressUnusable(),
549 DNSSDFlagsOption_Timeout(),
550
551 CLI_OPTION_GROUP( "Operation" ),
552 ConnectionOptions(),
553 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
554 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
555
556 #if( MDNSRESPONDER_PROJECT )
557 FallbackDNSServiceGroup(),
558 FallbackDNSServiceOption(),
559 #endif
560 ConnectionSection(),
561 CLI_OPTION_END()
562 };
563
564 //===========================================================================================================================
565 // QueryRecord Command Options
566 //===========================================================================================================================
567
568 static const char * gQueryRecord_Name = NULL;
569 static const char * gQueryRecord_Type = NULL;
570 static int gQueryRecord_OneShot = false;
571 static int gQueryRecord_TimeLimitSecs = 0;
572 static int gQueryRecord_RawRData = false;
573
574 static CLIOption kQueryRecordOpts[] =
575 {
576 InterfaceOption(),
577 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
578 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
579
580 CLI_OPTION_GROUP( "Flags" ),
581 DNSSDFlagsOption(),
582 DNSSDFlagsOption_AllowExpiredAnswers(),
583 DNSSDFlagsOption_DenyCellular(),
584 DNSSDFlagsOption_DenyConstrained(),
585 DNSSDFlagsOption_DenyExpensive(),
586 DNSSDFlagsOption_ForceMulticast(),
587 DNSSDFlagsOption_IncludeAWDL(),
588 DNSSDFlagsOption_PathEvalDone(),
589 DNSSDFlagsOption_ReturnIntermediates(),
590 DNSSDFlagsOption_SuppressUnusable(),
591 DNSSDFlagsOption_Timeout(),
592 DNSSDFlagsOption_UnicastResponse(),
593 DNSSDFlagsOption_EnableDNSSEC(),
594
595 CLI_OPTION_GROUP( "Operation" ),
596 ConnectionOptions(),
597 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
598 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
599 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
600
601 #if( MDNSRESPONDER_PROJECT )
602 FallbackDNSServiceGroup(),
603 FallbackDNSServiceOption(),
604 #endif
605 ConnectionSection(),
606 CLI_OPTION_END()
607 };
608
609 //===========================================================================================================================
610 // Register Command Options
611 //===========================================================================================================================
612
613 static const char * gRegister_Name = NULL;
614 static const char * gRegister_Type = NULL;
615 static const char * gRegister_Domain = NULL;
616 static int gRegister_Port = 0;
617 static const char * gRegister_TXT = NULL;
618 static int gRegister_LifetimeMs = -1;
619 static const char ** gAddRecord_Types = NULL;
620 static size_t gAddRecord_TypesCount = 0;
621 static const char ** gAddRecord_Data = NULL;
622 static size_t gAddRecord_DataCount = 0;
623 static const char ** gAddRecord_TTLs = NULL;
624 static size_t gAddRecord_TTLsCount = 0;
625 static const char * gUpdateRecord_Data = NULL;
626 static int gUpdateRecord_DelayMs = 0;
627 static int gUpdateRecord_TTL = 0;
628
629 static CLIOption kRegisterOpts[] =
630 {
631 InterfaceOption(),
632 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
633 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
634 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
635 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
636 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
637
638 CLI_OPTION_GROUP( "Flags" ),
639 DNSSDFlagsOption(),
640 DNSSDFlagsOption_IncludeAWDL(),
641 DNSSDFlagsOption_KnownUnique(),
642 DNSSDFlagsOption_NoAutoRename(),
643
644 CLI_OPTION_GROUP( "Operation" ),
645 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
646
647 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
648 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
649 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
650 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
651
652 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
653 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
654 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
655 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
656
657 RecordDataSection(),
658 CLI_OPTION_END()
659 };
660
661 //===========================================================================================================================
662 // RegisterRecord Command Options
663 //===========================================================================================================================
664
665 static const char * gRegisterRecord_Name = NULL;
666 static const char * gRegisterRecord_Type = NULL;
667 static const char * gRegisterRecord_Data = NULL;
668 static int gRegisterRecord_TTL = 0;
669 static int gRegisterRecord_LifetimeMs = -1;
670 static const char * gRegisterRecord_UpdateData = NULL;
671 static int gRegisterRecord_UpdateDelayMs = 0;
672 static int gRegisterRecord_UpdateTTL = 0;
673
674 static CLIOption kRegisterRecordOpts[] =
675 {
676 InterfaceOption(),
677 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
678 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
679 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
680 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
681
682 CLI_OPTION_GROUP( "Flags" ),
683 DNSSDFlagsOption(),
684 DNSSDFlagsOption_IncludeAWDL(),
685 DNSSDFlagsOption_KnownUnique(),
686 DNSSDFlagsOption_Shared(),
687 DNSSDFlagsOption_Unique(),
688
689 CLI_OPTION_GROUP( "Operation" ),
690 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
691
692 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
693 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
694 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
695 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
696
697 RecordDataSection(),
698 CLI_OPTION_END()
699 };
700
701 //===========================================================================================================================
702 // Resolve Command Options
703 //===========================================================================================================================
704
705 static char * gResolve_Name = NULL;
706 static char * gResolve_Type = NULL;
707 static char * gResolve_Domain = NULL;
708 static int gResolve_TimeLimitSecs = 0;
709
710 static CLIOption kResolveOpts[] =
711 {
712 InterfaceOption(),
713 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
714 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
715 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
716
717 CLI_OPTION_GROUP( "Flags" ),
718 DNSSDFlagsOption(),
719 DNSSDFlagsOption_ForceMulticast(),
720 DNSSDFlagsOption_IncludeAWDL(),
721 DNSSDFlagsOption_ReturnIntermediates(),
722 DNSSDFlagsOption_WakeOnResolve(),
723
724 CLI_OPTION_GROUP( "Operation" ),
725 ConnectionOptions(),
726 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
727
728 ConnectionSection(),
729 CLI_OPTION_END()
730 };
731
732 //===========================================================================================================================
733 // Reconfirm Command Options
734 //===========================================================================================================================
735
736 static const char * gReconfirmRecord_Name = NULL;
737 static const char * gReconfirmRecord_Type = NULL;
738 static const char * gReconfirmRecord_Class = NULL;
739 static const char * gReconfirmRecord_Data = NULL;
740
741 static CLIOption kReconfirmOpts[] =
742 {
743 InterfaceOption(),
744 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
745 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
746 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
747 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
748
749 CLI_OPTION_GROUP( "Flags" ),
750 DNSSDFlagsOption(),
751
752 RecordDataSection(),
753 CLI_OPTION_END()
754 };
755
756 //===========================================================================================================================
757 // getaddrinfo-POSIX Command Options
758 //===========================================================================================================================
759
760 static const char * gGAIPOSIX_HostName = NULL;
761 static const char * gGAIPOSIX_ServName = NULL;
762 static const char * gGAIPOSIX_Family = NULL;
763 static int gGAIPOSIXFlag_AddrConfig = false;
764 static int gGAIPOSIXFlag_All = false;
765 static int gGAIPOSIXFlag_CanonName = false;
766 static int gGAIPOSIXFlag_NumericHost = false;
767 static int gGAIPOSIXFlag_NumericServ = false;
768 static int gGAIPOSIXFlag_Passive = false;
769 static int gGAIPOSIXFlag_V4Mapped = false;
770 #if( defined( AI_V4MAPPED_CFG ) )
771 static int gGAIPOSIXFlag_V4MappedCFG = false;
772 #endif
773 #if( defined( AI_DEFAULT ) )
774 static int gGAIPOSIXFlag_Default = false;
775 #endif
776 #if( defined( AI_UNUSABLE ) )
777 static int gGAIPOSIXFlag_Unusable = false;
778 #endif
779
780 static CLIOption kGetAddrInfoPOSIXOpts[] =
781 {
782 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
783 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
784
785 CLI_OPTION_GROUP( "Hints" ),
786 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
787 "\n"
788 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
789 "address family is specified, then AF_UNSPEC is used.\n"
790 "\n" ),
791 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
792 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
793 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
794 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
795 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
796 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
797 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
798 #if( defined( AI_V4MAPPED_CFG ) )
799 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
800 #endif
801 #if( defined( AI_DEFAULT ) )
802 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
803 #endif
804 #if( defined( AI_UNUSABLE ) )
805 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
806 #endif
807
808 #if( MDNSRESPONDER_PROJECT )
809 FallbackDNSServiceGroup(),
810 FallbackDNSServiceOption(),
811 #endif
812 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
813 CLI_OPTION_END()
814 };
815
816 //===========================================================================================================================
817 // ReverseLookup Command Options
818 //===========================================================================================================================
819
820 static const char * gReverseLookup_IPAddr = NULL;
821 static int gReverseLookup_OneShot = false;
822 static int gReverseLookup_TimeLimitSecs = 0;
823
824 static CLIOption kReverseLookupOpts[] =
825 {
826 InterfaceOption(),
827 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
828
829 CLI_OPTION_GROUP( "Flags" ),
830 DNSSDFlagsOption(),
831 DNSSDFlagsOption_ForceMulticast(),
832 DNSSDFlagsOption_ReturnIntermediates(),
833 DNSSDFlagsOption_SuppressUnusable(),
834
835 CLI_OPTION_GROUP( "Operation" ),
836 ConnectionOptions(),
837 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
838 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
839
840 ConnectionSection(),
841 CLI_OPTION_END()
842 };
843
844 //===========================================================================================================================
845 // PortMapping Command Options
846 //===========================================================================================================================
847
848 static int gPortMapping_ProtocolTCP = false;
849 static int gPortMapping_ProtocolUDP = false;
850 static int gPortMapping_InternalPort = 0;
851 static int gPortMapping_ExternalPort = 0;
852 static int gPortMapping_TTL = 0;
853
854 static CLIOption kPortMappingOpts[] =
855 {
856 InterfaceOption(),
857 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
858 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
859 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
860 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
861 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
862
863 CLI_OPTION_GROUP( "Flags" ),
864 DNSSDFlagsOption(),
865
866 CLI_OPTION_GROUP( "Operation" ),
867 ConnectionOptions(),
868
869 ConnectionSection(),
870 CLI_OPTION_END()
871 };
872
873 #if( TARGET_OS_DARWIN )
874 //===========================================================================================================================
875 // RegisterKA Command Options
876 //===========================================================================================================================
877
878 static const char * gRegisterKA_LocalAddress = NULL;
879 static const char * gRegisterKA_RemoteAddress = NULL;
880 static int gRegisterKA_Timeout = 0;
881
882 static CLIOption kRegisterKA_Opts[] =
883 {
884 DNSSDFlagsOption(),
885 StringOption( 'l', "local", &gRegisterKA_LocalAddress, "IP addr+port", "TCP connection's local IPv4 or IPv6 address and port pair.", true ),
886 StringOption( 'r', "remote", &gRegisterKA_RemoteAddress, "IP addr+port", "TCP connection's remote IPv4 or IPv6 address and port pair.", true ),
887 IntegerOption( 't', "timeout", &gRegisterKA_Timeout, "timeout", "Keepalive record's timeout value, i.e., its 't=' value.", false ),
888 CLI_OPTION_END()
889 };
890
891 static void RegisterKACmd( void );
892 #endif
893
894 //===========================================================================================================================
895 // BrowseAll Command Options
896 //===========================================================================================================================
897
898 static const char * gBrowseAll_Domain = NULL;
899 static const char ** gBrowseAll_ServiceTypes = NULL;
900 static size_t gBrowseAll_ServiceTypesCount = 0;
901 static int gBrowseAll_BrowseTimeSecs = 5;
902 static int gBrowseAll_ConnectTimeout = 0;
903
904 static CLIOption kBrowseAllOpts[] =
905 {
906 InterfaceOption(),
907 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
908 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\". All services are browsed for if none is specified.", false ),
909
910 CLI_OPTION_GROUP( "Flags" ),
911 DNSSDFlagsOption_IncludeAWDL(),
912
913 CLI_OPTION_GROUP( "Operation" ),
914 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
915 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
916 CLI_OPTION_END()
917 };
918
919 //===========================================================================================================================
920 // GetNameInfo Command Options
921 //===========================================================================================================================
922
923 static void GetNameInfoCmd( void );
924
925 static char * gGetNameInfo_IPAddress = NULL;
926 static int gGetNameInfoFlag_DGram = false;
927 static int gGetNameInfoFlag_NameReqd = false;
928 static int gGetNameInfoFlag_NoFQDN = false;
929 static int gGetNameInfoFlag_NumericHost = false;
930 static int gGetNameInfoFlag_NumericScope = false;
931 static int gGetNameInfoFlag_NumericServ = false;
932
933 static CLIOption kGetNameInfoOpts[] =
934 {
935 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
936
937 CLI_OPTION_GROUP( "Flags" ),
938 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
939 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
940 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
941 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
942 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
943 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
944
945 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
946 CLI_OPTION_END()
947 };
948
949 //===========================================================================================================================
950 // GetAddrInfoStress Command Options
951 //===========================================================================================================================
952
953 static int gGAIStress_TestDurationSecs = 0;
954 static int gGAIStress_ConnectionCount = 0;
955 static int gGAIStress_DurationMinMs = 0;
956 static int gGAIStress_DurationMaxMs = 0;
957 static int gGAIStress_RequestCountMax = 0;
958
959 static CLIOption kGetAddrInfoStressOpts[] =
960 {
961 InterfaceOption(),
962
963 CLI_OPTION_GROUP( "Flags" ),
964 DNSSDFlagsOption_ReturnIntermediates(),
965 DNSSDFlagsOption_SuppressUnusable(),
966
967 CLI_OPTION_GROUP( "Operation" ),
968 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
969 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
970 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
971 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
972 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
973 CLI_OPTION_END()
974 };
975
976 //===========================================================================================================================
977 // DNSQuery Command Options
978 //===========================================================================================================================
979
980 static char * gDNSQuery_Name = NULL;
981 static char * gDNSQuery_Type = "A";
982 static char * gDNSQuery_Server = NULL;
983 static int gDNSQuery_TimeLimitSecs = 5;
984 static int gDNSQuery_UseTCP = false;
985 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
986 static int gDNSQuery_DNSSEC = false;
987 static int gDNSQuery_CheckingDisabled = false;
988 static int gDNSQuery_RawRData = false;
989 static int gDNSQuery_Verbose = false;
990
991 #if( TARGET_OS_DARWIN )
992 #define kDNSQueryServerOptionIsRequired false
993 #else
994 #define kDNSQueryServerOptionIsRequired true
995 #endif
996
997 static CLIOption kDNSQueryOpts[] =
998 {
999 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
1000 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. (default: A)", false ),
1001 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
1002 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
1003 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
1004 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. (default: 0x0100 [Recursion Desired])", false ),
1005 BooleanOption( 0 , "dnssec", &gDNSQuery_DNSSEC, "Set the AD bit and include OPT record with DO extended flag bit set." ),
1006 BooleanOption( 0 , "checkingDisabled", &gDNSQuery_CheckingDisabled, "Set the Checking Disabled (CD) bit." ),
1007 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
1008 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
1009 CLI_OPTION_END()
1010 };
1011
1012 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1013 //===========================================================================================================================
1014 // DNSCrypt Command Options
1015 //===========================================================================================================================
1016
1017 static char * gDNSCrypt_ProviderName = NULL;
1018 static char * gDNSCrypt_ProviderKey = NULL;
1019 static char * gDNSCrypt_Name = NULL;
1020 static char * gDNSCrypt_Type = NULL;
1021 static char * gDNSCrypt_Server = NULL;
1022 static int gDNSCrypt_TimeLimitSecs = 5;
1023 static int gDNSCrypt_RawRData = false;
1024 static int gDNSCrypt_Verbose = false;
1025
1026 static CLIOption kDNSCryptOpts[] =
1027 {
1028 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
1029 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
1030 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
1031 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
1032 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
1033 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
1034 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
1035 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
1036 CLI_OPTION_END()
1037 };
1038 #endif
1039
1040 //===========================================================================================================================
1041 // MDNSQuery Command Options
1042 //===========================================================================================================================
1043
1044 static char * gMDNSQuery_Name = NULL;
1045 static char * gMDNSQuery_Type = NULL;
1046 static int gMDNSQuery_SourcePort = 0;
1047 static int gMDNSQuery_IsQU = false;
1048 static int gMDNSQuery_RawRData = false;
1049 static int gMDNSQuery_UseIPv4 = false;
1050 static int gMDNSQuery_UseIPv6 = false;
1051 static int gMDNSQuery_AllResponses = false;
1052 static int gMDNSQuery_ReceiveSecs = 1;
1053
1054 static CLIOption kMDNSQueryOpts[] =
1055 {
1056 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1057 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
1058 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
1059 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
1060 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
1061 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
1062 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
1063 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
1064 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
1065 IntegerOption( 'r', "receiveTime", &gMDNSQuery_ReceiveSecs, "seconds", "Amount of time to spend receiving messages after the query is sent. The default is one second. Use -1 for unlimited time.", false ),
1066 CLI_OPTION_END()
1067 };
1068
1069 //===========================================================================================================================
1070 // MDNSCollider Command Options
1071 //===========================================================================================================================
1072
1073 #define kMDNSColliderProgramSection_Intro \
1074 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
1075 "ought to react to probe queries that match its record's name, if at all.\n" \
1076 "\n" \
1077 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
1078 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
1079 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
1080 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
1081 "accomplished with the following program:\n" \
1082 "\n" \
1083 " probes 3r; send; wait 5000\n" \
1084 "\n" \
1085 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
1086 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
1087 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
1088 "is more than enough time for the collider to respond to probe queries.\n" \
1089 "\n" \
1090 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
1091 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
1092 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1093
1094 #define kMDNSColliderProgramSection_Probes \
1095 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
1096 "\n" \
1097 "Usage: probes [<action-string>]\n" \
1098 "\n" \
1099 "The syntax for an action-string is\n" \
1100 "\n" \
1101 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
1102 " <action> ::= [<repeat-count>] <action-code>\n" \
1103 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
1104 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
1105 "\n" \
1106 "An expanded action-string is defined as\n" \
1107 "\n" \
1108 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
1109 "\n" \
1110 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
1111 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
1112 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
1113 "are not allowed.\n" \
1114 "\n" \
1115 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
1116 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
1117 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
1118 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
1119 "\n" \
1120 " Code Action\n" \
1121 " ---- ------\n" \
1122 " n Do nothing.\n" \
1123 " r Respond to the probe query.\n" \
1124 " u Respond to the probe query via unicast.\n" \
1125 " m Respond to the probe query via multicast.\n" \
1126 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1127 "\n" \
1128 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1129 "a response nor does it multicast its own probe query.\n"
1130
1131 #define kMDNSColliderProgramSection_Send \
1132 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1133 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1134 "\n" \
1135 "Usage: send\n"
1136
1137 #define kMDNSColliderProgramSection_Wait \
1138 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1139 "\n" \
1140 "Usage: wait <milliseconds>\n"
1141
1142 #define kMDNSColliderProgramSection_Loop \
1143 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1144 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1145 "\n" \
1146 "Usage: loop <non-zero count>; ... ; done\n" \
1147 "\n" \
1148 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1149 "\n" \
1150 " loop 3; wait 1000; send; done"
1151
1152 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1153
1154 static const char * gMDNSCollider_Name = NULL;
1155 static const char * gMDNSCollider_Type = NULL;
1156 static const char * gMDNSCollider_RecordData = NULL;
1157 static int gMDNSCollider_UseIPv4 = false;
1158 static int gMDNSCollider_UseIPv6 = false;
1159 static const char * gMDNSCollider_Program = NULL;
1160
1161 static CLIOption kMDNSColliderOpts[] =
1162 {
1163 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1164 StringOption( 'n', "name", &gMDNSCollider_Name, "name", "Collider's record name.", true ),
1165 StringOption( 't', "type", &gMDNSCollider_Type, "type", "Collider's record type.", true ),
1166 StringOption( 'd', "data", &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1167 StringOption( 'p', "program", &gMDNSCollider_Program, "program", "Program to execute. See Program section below.", true ),
1168 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4, "Use IPv4." ),
1169 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6, "Use IPv6." ),
1170
1171 RecordDataSection(),
1172 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro ),
1173 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes ),
1174 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send ),
1175 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait ),
1176 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop ),
1177 CLI_OPTION_END()
1178 };
1179
1180 static void MDNSColliderCmd( void );
1181
1182 #if( TARGET_OS_DARWIN )
1183 //===========================================================================================================================
1184 // PIDToUUID Command Options
1185 //===========================================================================================================================
1186
1187 static int gPIDToUUID_PID = 0;
1188
1189 static CLIOption kPIDToUUIDOpts[] =
1190 {
1191 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1192 CLI_OPTION_END()
1193 };
1194 #endif
1195
1196 //===========================================================================================================================
1197 // DNSServer Command Options
1198 //===========================================================================================================================
1199
1200 static const char kDNSServerInfoText_Intro[] =
1201 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n"
1202 "presence of special labels in the query's QNAME. There are currently nine types of special labels that can be\n"
1203 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n"
1204 "IPv4 label, the IPv6 label, Index labels, and SRV labels.\n"
1205 "\n"
1206 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n";
1207
1208 static const char kDNSServerInfoText_NameExistence[] =
1209 "A name is considered to exist if it's an Address name or an SRV name.\n"
1210 "\n"
1211 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n"
1212 "order, unless otherwise noted, consist of\n"
1213 "\n"
1214 " 1. at most one Alias or Alias-TTL label as the first label;\n"
1215 " 2. at most one Count label;\n"
1216 " 3. zero or more Tag labels;\n"
1217 " 4. at most one TTL label; and\n"
1218 " 5. at most one IPv4 or IPv6 label.\n"
1219 " 6. at most one Index label.\n"
1220 "\n"
1221 "An SRV name is defined as a name with the following form:\n"
1222 "\n"
1223 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n"
1224 "\n"
1225 "See \"SRV Names\" for details.\n";
1226
1227 static const char kDNSServerInfoText_ResourceRecords[] =
1228 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n"
1229 "\n"
1230 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n"
1231 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n"
1232 "\n"
1233 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n"
1234 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n"
1235 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n"
1236 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n"
1237 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n"
1238 "\n"
1239 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n"
1240 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n"
1241 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n"
1242 "\n"
1243 "SRV names are names of SRV records.\n"
1244 "\n"
1245 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n"
1246 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n"
1247 "AAAA records with specific TTL values.\n";
1248
1249 static const char kDNSServerInfoText_AliasLabel[] =
1250 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n"
1251 "\n"
1252 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n"
1253 "exactly N CNAME records:\n"
1254 "\n"
1255 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n"
1256 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n"
1257 " name has \"alias-(i - 1)\" as its first label.\n"
1258 "\n"
1259 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1260 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n"
1261 " \"alias\" instead.\n"
1262 "\n"
1263 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1264 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n"
1265 "\n"
1266 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n"
1267 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n"
1268 "QNAME minus its first label.\n"
1269 "\n"
1270 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n"
1271 "records:\n"
1272 "\n"
1273 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n"
1274 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n"
1275 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n"
1276 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n";
1277
1278 static const char kDNSServerInfoText_AliasTTLLabel[] =
1279 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n"
1280 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n"
1281 "\n"
1282 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n"
1283 "will contain exactly N CNAME records:\n"
1284 "\n"
1285 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n"
1286 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n"
1287 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n"
1288 "\n"
1289 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1290 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n"
1291 " label.\n"
1292 "\n"
1293 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n"
1294 "CNAME records:\n"
1295 "\n"
1296 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n"
1297 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n"
1298 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n";
1299
1300 static const char kDNSServerInfoText_CountLabel[] =
1301 "Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [0, 255] and N_2 is\n"
1302 "an integer in [N_1, 255].\n"
1303 "\n"
1304 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n"
1305 "QTYPE, then the response will contain exactly N address records:\n"
1306 "\n"
1307 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n"
1308 " and whose RDATA is an address equal to a constant base address + i.\n"
1309 "\n"
1310 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n"
1311 "\n"
1312 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n"
1313 "records:\n"
1314 "\n"
1315 " count-3.d.test. 60 IN A 203.0.113.1\n"
1316 " count-3.d.test. 60 IN A 203.0.113.2\n"
1317 " count-3.d.test. 60 IN A 203.0.113.3\n"
1318 "\n"
1319 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n"
1320 "specified by QTYPE, then the response will contain exactly N_1 address records:\n"
1321 "\n"
1322 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n"
1323 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n"
1324 "\n"
1325 " 2. The order of the address records will be random.\n"
1326 "\n"
1327 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n"
1328 "following AAAA records:\n"
1329 "\n"
1330 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n"
1331 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n"
1332 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n"
1333 "\n"
1334 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n"
1335 "will contain no address records, regardless of whether it contains a Count label.\n"
1336 "\n"
1337 "Address names that don't have a Count label are treated as though they contain a count label equal to\n"
1338 "count-1\".\n";
1339
1340 static const char kDNSServerInfoText_TagLabel[] =
1341 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n"
1342 "\n"
1343 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n"
1344 "to increase the sizes of domain names.\n";
1345
1346 static const char kDNSServerInfoText_TTLLabel[] =
1347 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n"
1348 "\n"
1349 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n"
1350 "response will have a TTL value equal to T.\n";
1351
1352 static const char kDNSServerInfoText_IPv4Label[] =
1353 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n";
1354
1355 static const char kDNSServerInfoText_IPv6Label[] =
1356 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n";
1357
1358 static const char kDNSServerInfoText_IndexLabel[] =
1359 "Index labels are of the form \"index-N\", where N is an integer in [1, 2^31 - 1].\n"
1360 "\n"
1361 "When the server runs in loopback-only mode, each of the server's addresses is assigned a sequential index value\n"
1362 "starting from 1. For example, if the server is running in loopback-only mode and listening exclusively on IPv6\n"
1363 "with two extra IPv6 addresses, then address ::1 would be assigned index 1, the first extra IPv6 address would be\n"
1364 "assigned index 2, and the second extra IPv6 address would be assigned index 3.\n"
1365 "\n"
1366 "If QNAME is an Address name and has an index label, then the query will be ignored unless the query was received\n"
1367 "on an address whose index value equals that of the index label. This is useful for simulating unresponsive servers.\n";
1368
1369 static const char kDNSServerInfoText_SRVNames[] =
1370 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n"
1371 "\n"
1372 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n"
1373 "leading up to the the first SRV label, if one exists, or the d.test. labels will be used as a parent domain for\n"
1374 "the target hostname of each of the SRV name's SRV records.\n"
1375 "\n"
1376 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n"
1377 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n"
1378 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n"
1379 "d.test. labels, whichever comes first.\n"
1380 "\n"
1381 "Example. A response to an SRV record query with a QNAME of\n"
1382 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n"
1383 "\n"
1384 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. 60 IN SRV 0 0 80 www.example.com.\n"
1385 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. 60 IN SRV 1 0 8080 www.example.com.\n";
1386
1387 static const char kDNSServerInfoText_BadUDPMode[] =
1388 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n"
1389 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n"
1390 "that's not equal to the query's message ID.\n"
1391 "\n"
1392 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n"
1393 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n"
1394 "IPv4 addresses in the 0.0.0.0/24 block instead of the 203.0.113.0/24 block, i.e., 0.0.0.0 is used as the IPv4\n"
1395 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n"
1396 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n";
1397
1398 static int gDNSServer_LoopbackOnly = false;
1399 static int gDNSServer_Foreground = false;
1400 static int gDNSServer_ResponseDelayMs = 0;
1401 static int gDNSServer_DefaultTTL = 60;
1402 static int gDNSServer_Port = kDNSPort;
1403 static const char * gDNSServer_DomainOverride = NULL;
1404 static char ** gDNSServer_IgnoredQTypes = NULL;
1405 static size_t gDNSServer_IgnoredQTypesCount = 0;
1406 static int gDNSServer_ListenOnV4 = false;
1407 static int gDNSServer_ListenOnV6 = false;
1408 static int gDNSServer_BadUDPMode = false;
1409 #if( TARGET_OS_DARWIN )
1410 static const char * gDNSServer_FollowPID = NULL;
1411 static int gDNSServer_ExtraV6Count = 0;
1412 #endif
1413
1414 static CLIOption kDNSServerOpts[] =
1415 {
1416 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind only to the loopback interface." ),
1417 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Direct log output to stdout instead of system logging." ),
1418 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1419 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1420 IntegerOption( 'p', "port", &gDNSServer_Port, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1421 StringOption( 0 , "domain", &gDNSServer_DomainOverride, "domain", "Use to override 'd.test.' as the server's domain.", false ),
1422 MultiStringOption( 'i', "ignoreQType", &gDNSServer_IgnoredQTypes, &gDNSServer_IgnoredQTypesCount, "qtype", "A QTYPE to ignore. This option can be specified more than once.", false ),
1423 BooleanOption( 0 , "ipv4", &gDNSServer_ListenOnV4, "Listen on IPv4. Will listen on both IPv4 and IPv6 if neither --ipv4 nor --ipv6 is used." ),
1424 BooleanOption( 0 , "ipv6", &gDNSServer_ListenOnV6, "Listen on IPv6. Will listen on both IPv4 and IPv6 if neither --ipv4 nor --ipv6 is used." ),
1425 #if( TARGET_OS_DARWIN )
1426 StringOption( 0 , "follow", &gDNSServer_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1427 #endif
1428 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1429
1430 #if( TARGET_OS_DARWIN )
1431 CLI_OPTION_GROUP( "Loopback-Only Mode Options" ),
1432 IntegerOptionEx( 0 , "extraIPv6", &gDNSServer_ExtraV6Count, "count", "The number of extra IPv6 addresses to listen on. (default: 0)", false,
1433 "\n"
1434 "This option will add extra IPv6 addresses from the fded:f035:55e4::/64 address block to the loopback interface.\n"
1435 "The server will then bind to those addresses in addition to the standard loopback IP addresses, i.e., 127.0.0.1.\n"
1436 "and/or ::1, depending on the specified IP protocol options.\n"
1437 "\n"
1438 "This option is useful for setting up a DNS configuration with multiple server addresses, e.g., one for the\n"
1439 "primary server, one for the secondary server, etc. The Index label can then be used to simulate unresponsive\n"
1440 "servers.\n"
1441 "\n"
1442 "Note: This option is ignored unless the server is in loopback only mode and listening on IPv6.\n"
1443 "Note: This option currently requires root privileges.\n"
1444 ),
1445 #endif
1446 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1447 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1448 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1449 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1450 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1451 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1452 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1453 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1454 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1455 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1456 CLI_SECTION( "Index Labels", kDNSServerInfoText_IndexLabel ),
1457 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames ),
1458 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode ),
1459 CLI_OPTION_END()
1460 };
1461
1462 static void DNSServerCmd( void );
1463
1464 //===========================================================================================================================
1465 // MDNSReplier Command Options
1466 //===========================================================================================================================
1467
1468 #define kMDNSReplierPortBase 50000
1469
1470 static const char kMDNSReplierInfoText_Intro[] =
1471 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n"
1472 "PTR, SRV, TXT, A, and AAAA as described below.\n"
1473 "\n"
1474 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n";
1475
1476 static const char kMDNSReplierInfoText_Parameters[] =
1477 "There are five parameters that control the replier's set of authoritative records.\n"
1478 "\n"
1479 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n"
1480 " parameter is specified with the --hostname option.\n"
1481 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n"
1482 " option.\n"
1483 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n"
1484 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n"
1485 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n"
1486 " --maxInstanceCount option.\n"
1487 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n"
1488 " with the --countA option.\n"
1489 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n"
1490 " specified with the --countAAAA option.\n";
1491
1492 static const char kMDNSReplierInfoText_PTR[] =
1493 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n"
1494 "integer in [1, 65535], and N is an integer in [1, N_max].\n"
1495 "\n"
1496 "For a given L and N, the replier has exactly N authoritative PTR records:\n"
1497 "\n"
1498 " 1. The first PTR record is defined as\n"
1499 "\n"
1500 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n"
1501 " TYPE: PTR\n"
1502 " CLASS: IN\n"
1503 " TTL: 4500\n"
1504 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1505 "\n"
1506 " 2. For each i in [2, N], there is one PTR record defined as\n"
1507 "\n"
1508 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n"
1509 " TYPE: PTR\n"
1510 " CLASS: IN\n"
1511 " TTL: 4500\n"
1512 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n";
1513
1514 static const char kMDNSReplierInfoText_SRV[] =
1515 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n"
1516 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n"
1517 "\"<hostname> (<i>)\", where i is in [2, N].\n"
1518 "\n"
1519 "For a given L and N, the replier has exactly N authoritative SRV records:\n"
1520 "\n"
1521 " 1. The first SRV record is defined as\n"
1522 "\n"
1523 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1524 " TYPE: SRV\n"
1525 " CLASS: IN\n"
1526 " TTL: 120\n"
1527 " RDATA:\n"
1528 " Priority: 0\n"
1529 " Weight: 0\n"
1530 " Port: (50000 + L) mod 2^16\n"
1531 " Target: <hostname>.local.\n"
1532 "\n"
1533 " 2. For each i in [2, N], there is one SRV record defined as:\n"
1534 "\n"
1535 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1536 " TYPE: SRV\n"
1537 " CLASS: IN\n"
1538 " TTL: 120\n"
1539 " RDATA:\n"
1540 " Priority: 0\n"
1541 " Weight: 0\n"
1542 " Port: (50000 + L) mod 2^16\n"
1543 " Target: <hostname>-<i>.local.\n";
1544
1545 static const char kMDNSReplierInfoText_TXT[] =
1546 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n"
1547 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n"
1548 "\"<hostname> (<i>)\", where i is in [2, N].\n"
1549 "\n"
1550 "For a given L and N, the replier has exactly N authoritative TXT records:\n"
1551 "\n"
1552 " 1. The first TXT record is defined as\n"
1553 "\n"
1554 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1555 " TYPE: TXT\n"
1556 " CLASS: IN\n"
1557 " TTL: 4500\n"
1558 " RDLENGTH: L\n"
1559 " RDATA: <one or more strings with an aggregate length of L octets>\n"
1560 "\n"
1561 " 2. For each i in [2, N], there is one TXT record:\n"
1562 "\n"
1563 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1564 " TYPE: TXT\n"
1565 " CLASS: IN\n"
1566 " TTL: 4500\n"
1567 " RDLENGTH: L\n"
1568 " RDATA: <one or more strings with an aggregate length of L octets>\n"
1569 "\n"
1570 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n"
1571 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n"
1572 "the string may be truncated to satisfy the TXT record data's size requirement.\n";
1573
1574 static const char kMDNSReplierInfoText_A[] =
1575 "The replier has exactly N_max ✕ N_a authoritative A records:\n"
1576 "\n"
1577 " For each i in [1, N_max], for each j in [1, N_a], an A record is defined as\n"
1578 "\n"
1579 " NAME: \"<hostname>.local.\" if i = 1, otherwise \"<hostname>-<i>.local.\"\n"
1580 " TYPE: A\n"
1581 " CLASS: IN\n"
1582 " TTL: 120\n"
1583 " RDLENGTH: 4\n"
1584 " RDATA: 0.<⌊i / 256⌋>.<i mod 256>.<j>\n";
1585
1586 static const char kMDNSReplierInfoText_AAAA[] =
1587 "The replier has exactly N_max ✕ N_aaaa authoritative AAAA records:\n"
1588 "\n"
1589 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n"
1590 "\n"
1591 " NAME: <hostname>.local.\n"
1592 " TYPE: AAAA\n"
1593 " CLASS: IN\n"
1594 " TTL: 120\n"
1595 " RDLENGTH: 16\n"
1596 " RDATA: fe80::1:<j>\n"
1597 "\n"
1598 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n"
1599 "\n"
1600 " NAME: <hostname>-<i>.local.\n"
1601 " TYPE: AAAA\n"
1602 " CLASS: IN\n"
1603 " TTL: 120\n"
1604 " RDLENGTH: 16\n"
1605 " RDATA: 2001:db8:2::<i>:<j>\n";
1606
1607 static const char kMDNSReplierInfoText_Responses[] =
1608 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n"
1609 "together in the same response message, and any two records pertaining to different hostnames will be in\n"
1610 "separate response messages.\n";
1611
1612 static const char * gMDNSReplier_Hostname = NULL;
1613 static const char * gMDNSReplier_ServiceTypeTag = NULL;
1614 static int gMDNSReplier_MaxInstanceCount = 1000;
1615 static int gMDNSReplier_NoAdditionals = false;
1616 static int gMDNSReplier_RecordCountA = 1;
1617 static int gMDNSReplier_RecordCountAAAA = 1;
1618 static double gMDNSReplier_UnicastDropRate = 0.0;
1619 static double gMDNSReplier_MulticastDropRate = 0.0;
1620 static int gMDNSReplier_MaxDropCount = 0;
1621 static int gMDNSReplier_UseIPv4 = false;
1622 static int gMDNSReplier_UseIPv6 = false;
1623 static int gMDNSReplier_Foreground = false;
1624 #if( TARGET_OS_POSIX )
1625 static const char * gMDNSReplier_FollowPID = NULL;
1626 #endif
1627
1628 static CLIOption kMDNSReplierOpts[] =
1629 {
1630 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1631 StringOption( 'n', "hostname", &gMDNSReplier_Hostname, "string", "Base name to use for hostnames and service instance names.", true ),
1632 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1633 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount, "count", "Maximum number of service instances. (default: 1000)", false ),
1634 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals, "When answering queries, don't include any additional records." ),
1635 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1636 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1637 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1638 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1639 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1640 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4, "Use IPv4." ),
1641 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6, "Use IPv6." ),
1642 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground, "Direct log output to stdout instead of system logging." ),
1643 #if( TARGET_OS_POSIX )
1644 StringOption( 0 , "follow", &gMDNSReplier_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1645 #endif
1646
1647 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro ),
1648 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters ),
1649 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR ),
1650 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV ),
1651 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT ),
1652 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A ),
1653 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA ),
1654 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses ),
1655 CLI_OPTION_END()
1656 };
1657
1658 static void MDNSReplierCmd( void );
1659
1660 //===========================================================================================================================
1661 // Test Command Options
1662 //===========================================================================================================================
1663
1664 #define kTestExitStatusSection_Name "Exit Status"
1665 #define kTestExitStatusSection_Text \
1666 "This test command can exit with one of three status codes:\n" \
1667 "\n" \
1668 "0 - The test ran to completion and passed.\n" \
1669 "1 - A fatal error prevented the test from completing.\n" \
1670 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1671 "\n" \
1672 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1673 "performance.\n"
1674
1675 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1676
1677 #define kGAIPerfTestSuiteName_Basic "basic"
1678 #define kGAIPerfTestSuiteName_Advanced "advanced"
1679
1680 static const char * gGAIPerf_TestSuite = NULL;
1681 static int gGAIPerf_CallDelayMs = 10;
1682 static int gGAIPerf_ServerDelayMs = 10;
1683 static int gGAIPerf_SkipPathEvalulation = false;
1684 static int gGAIPerf_BadUDPMode = false;
1685 static int gGAIPerf_IterationCount = 100;
1686 static int gGAIPerf_IterationTimeLimitMs = 100;
1687 static const char * gGAIPerf_OutputFilePath = NULL;
1688 static const char * gGAIPerf_OutputFormat = kOutputFormatStr_JSON;
1689 static int gGAIPerf_OutputAppendNewline = false;
1690
1691 static void GAIPerfCmd( void );
1692
1693 #define kGAIPerfSectionText_TestSuiteBasic \
1694 "This test suite consists of the following three test cases:\n" \
1695 "\n" \
1696 "Test Case #1: Resolve a domain name with\n" \
1697 "\n" \
1698 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1699 "\n" \
1700 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1701 "server queries.\n" \
1702 "\n" \
1703 "Test Case #2: Resolve a domain name with\n" \
1704 "\n" \
1705 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1706 "\n" \
1707 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1708 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1709 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1710 "\n" \
1711 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1712 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1713 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1714 "\n" \
1715 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1716
1717 #define kGAIPerfSectionText_TestSuiteAdvanced \
1718 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1719 "\n" \
1720 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1721 "\n" \
1722 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1723 "\n" \
1724 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1725 "server queries.\n" \
1726 "\n" \
1727 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1728 "\n" \
1729 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1730 "\n" \
1731 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1732 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1733 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1734 "\n" \
1735 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1736 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1737 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1738 "\n" \
1739 "N_c and N_a take on the following values, depending on the value of N:\n" \
1740 "\n" \
1741 " N_c is 0 if N is in [1, 8].\n" \
1742 " N_c is 1 if N is in [9, 16].\n" \
1743 " N_c is 2 if N is in [17, 24].\n" \
1744 " N_c is 4 if N is in [25, 32].\n" \
1745 "\n" \
1746 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1747 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1748 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1749 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1750 "\n" \
1751 "Finally,\n" \
1752 "\n" \
1753 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1754
1755 static CLIOption kGAIPerfOpts[] =
1756 {
1757 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1758 "\n"
1759 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1760 "\n"
1761 ),
1762 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1763 FormatOption( 'f', "format", &gGAIPerf_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1764 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1765 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount, "count", "The number of iterations per test case. (default: 100)", false ),
1766 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1767 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1768 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1769
1770 CLI_OPTION_GROUP( "DNS Server Options" ),
1771 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1772 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1773
1774 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic ),
1775 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced ),
1776 TestExitStatusSection(),
1777 CLI_OPTION_END()
1778 };
1779
1780 static void MDNSDiscoveryTestCmd( void );
1781
1782 static int gMDNSDiscoveryTest_InstanceCount = 100;
1783 static int gMDNSDiscoveryTest_TXTSize = 100;
1784 static int gMDNSDiscoveryTest_BrowseTimeSecs = 2;
1785 static int gMDNSDiscoveryTest_FlushCache = false;
1786 static char * gMDNSDiscoveryTest_Interface = NULL;
1787 static int gMDNSDiscoveryTest_NoAdditionals = false;
1788 static int gMDNSDiscoveryTest_RecordCountA = 1;
1789 static int gMDNSDiscoveryTest_RecordCountAAAA = 1;
1790 static double gMDNSDiscoveryTest_UnicastDropRate = 0.0;
1791 static double gMDNSDiscoveryTest_MulticastDropRate = 0.0;
1792 static int gMDNSDiscoveryTest_MaxDropCount = 0;
1793 static int gMDNSDiscoveryTest_UseIPv4 = false;
1794 static int gMDNSDiscoveryTest_UseIPv6 = false;
1795 #if( MDNSRESPONDER_PROJECT )
1796 static int gMDNSDiscoveryTest_UseNewGAI = false;
1797 #endif
1798 static const char * gMDNSDiscoveryTest_OutputFormat = kOutputFormatStr_JSON;
1799 static int gMDNSDiscoveryTest_OutputAppendNewline = false;
1800 static const char * gMDNSDiscoveryTest_OutputFilePath = NULL;
1801
1802 static CLIOption kMDNSDiscoveryTestOpts[] =
1803 {
1804 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount, "count", "Number of service instances to discover. (default: 100)", false ),
1805 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1806 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1807 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1808
1809 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1810 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1811 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals, "When answering queries, don't include any additional records." ),
1812 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1813 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1814 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1815 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1816 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1817 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4, "Use IPv4." ),
1818 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6, "Use IPv6." ),
1819 #if( MDNSRESPONDER_PROJECT )
1820 BooleanOption( 0 , "useNewGAI", &gMDNSDiscoveryTest_UseNewGAI, "Use dnssd_getaddrinfo_* instead of DNSServiceGetAddrInfo()." ),
1821 #endif
1822
1823 CLI_OPTION_GROUP( "Results" ),
1824 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1825 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1826
1827 TestExitStatusSection(),
1828 CLI_OPTION_END()
1829 };
1830
1831 static void DotLocalTestCmd( void );
1832
1833 static const char * gDotLocalTest_Interface = NULL;
1834 static const char * gDotLocalTest_OutputFormat = kOutputFormatStr_JSON;
1835 static const char * gDotLocalTest_OutputFilePath = NULL;
1836
1837 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1838 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1839 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1840 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1841 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1842 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1843 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1844
1845 #define kDotLocalTestSectionText_Description \
1846 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1847 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1848 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1849 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1850 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1851 "then the mdnsreplier's base hostname is test.local.\n" \
1852 "\n" \
1853 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1854 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1855 "\n" \
1856 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1857 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1858 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1859 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1860 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1861 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1862 "\n" \
1863 "Each subtest runs for five seconds.\n"
1864
1865 static CLIOption kDotLocalTestOpts[] =
1866 {
1867 StringOption( 'i', "interface", &gDotLocalTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1868
1869 CLI_OPTION_GROUP( "Results" ),
1870 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1871 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1872
1873 CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1874 TestExitStatusSection(),
1875 CLI_OPTION_END()
1876 };
1877
1878 static void ProbeConflictTestCmd( void );
1879
1880 static const char * gProbeConflictTest_Interface = NULL;
1881 static int gProbeConflictTest_UseComputerName = false;
1882 static int gProbeConflictTest_UseIPv4 = false;
1883 static int gProbeConflictTest_UseIPv6 = false;
1884 static const char * gProbeConflictTest_OutputFormat = kOutputFormatStr_JSON;
1885 static const char * gProbeConflictTest_OutputFilePath = NULL;
1886
1887 static CLIOption kProbeConflictTestOpts[] =
1888 {
1889 StringOption( 'i', "interface", &gProbeConflictTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1890 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1891 BooleanOption( 0 , "ipv4", &gProbeConflictTest_UseIPv4, "Use IPv4 instead of IPv6. (Default behavior.)" ),
1892 BooleanOption( 0 , "ipv6", &gProbeConflictTest_UseIPv6, "Use IPv6 instead of IPv4." ),
1893
1894 CLI_OPTION_GROUP( "Results" ),
1895 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat, "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1896 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1897
1898 TestExitStatusSection(),
1899 CLI_OPTION_END()
1900 };
1901
1902 static void RegistrationTestCmd( void );
1903
1904 static int gRegistrationTest_BATSEnvironment = false;
1905 static const char * gRegistrationTest_OutputFormat = kOutputFormatStr_JSON;
1906 static const char * gRegistrationTest_OutputFilePath = NULL;
1907
1908 static CLIOption kRegistrationTestOpts[] =
1909 {
1910 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment, "Informs the test that it's running in a BATS environment.",
1911 "\n"
1912 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1913 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1914 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1915 "\n"
1916 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1917 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1918 ),
1919 CLI_OPTION_GROUP( "Results" ),
1920 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1921 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1922
1923 TestExitStatusSection(),
1924 CLI_OPTION_END()
1925 };
1926
1927 #if( MDNSRESPONDER_PROJECT )
1928 static void FallbackTestCmd( void );
1929
1930 static int gFallbackTest_UseRefused = false;
1931 static const char * gFallbackTest_OutputFormat = kOutputFormatStr_JSON;
1932 static const char * gFallbackTest_OutputFilePath = NULL;
1933
1934 static CLIOption kFallbackTestOpts[] =
1935 {
1936 BooleanOption( 0 , "useRefused", &gFallbackTest_UseRefused, "Have the server use the Refused RCODE in responses when a query is not allowed to be answered." ),
1937 CLI_OPTION_GROUP( "Results" ),
1938 FormatOption( 'f', "format", &gFallbackTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1939 StringOption( 'o', "output", &gFallbackTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1940
1941 TestExitStatusSection(),
1942 CLI_OPTION_END()
1943 };
1944
1945 static void ExpensiveConstrainedTestCmd( void );
1946
1947 static const char * gExpensiveConstrainedTest_Interface = NULL;
1948 static const char * gExpensiveConstrainedTest_Name = NULL;
1949 static Boolean gExpensiveConstrainedTest_DenyExpensive = false;
1950 static Boolean gExpensiveConstrainedTest_DenyConstrained = false;
1951 static Boolean gExpensiveConstrainedTest_StartFromExpensive = false;
1952 static int gExpensiveConstrainedTest_ProtocolIPv4 = false;
1953 static int gExpensiveConstrainedTest_ProtocolIPv6 = false;
1954 static const char * gExpensiveConstrainedTest_OutputFormat = kOutputFormatStr_JSON;
1955 static const char * gExpensiveConstrainedTest_OutputFilePath = NULL;
1956
1957 static CLIOption kExpensiveConstrainedTestOpts[] =
1958 {
1959 CLI_OPTION_GROUP( "Results" ),
1960 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1961 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1962
1963 TestExitStatusSection(),
1964 CLI_OPTION_END()
1965 };
1966
1967 static void DNSProxyTestCmd( void );
1968
1969 static const char * gDNSProxyTest_OutputFormat = kOutputFormatStr_JSON;
1970 static const char * gDNSProxyTest_OutputFilePath = NULL;
1971
1972 static CLIOption kDNSProxyTestOpts[] =
1973 {
1974 CLI_OPTION_GROUP( "Results" ),
1975 FormatOption( 'f', "format", &gDNSProxyTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1976 StringOption( 'o', "output", &gDNSProxyTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1977
1978 TestExitStatusSection(),
1979 CLI_OPTION_END()
1980 };
1981
1982 static void RCodeTestCmd( void );
1983
1984 static const char * gRCodeTest_OutputFormat = kOutputFormatStr_JSON;
1985 static const char * gRCodeTest_OutputFilePath = NULL;
1986
1987 static CLIOption kRCodeTestOpts[] =
1988 {
1989 CLI_OPTION_GROUP( "Results" ),
1990 FormatOption( 'f', "format", &gRCodeTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1991 StringOption( 'o', "output", &gRCodeTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1992
1993 TestExitStatusSection(),
1994 CLI_OPTION_END()
1995 };
1996
1997 static void XCTestCmd( void );
1998
1999 static const char * gXCTest_Classname = NULL;
2000
2001 static CLIOption kXCTestOpts[] =
2002 {
2003 StringOption( 'c', "class", &gXCTest_Classname, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
2004 CLI_OPTION_END()
2005 };
2006
2007 static void MultiConnectTestCmd( void );
2008
2009 static int gMultiConnectTest_ConnectionCount = 4; // default to 4
2010
2011 static CLIOption kMultiConnectTestOpts[] =
2012 {
2013 IntegerOption( 0, "connections", &gMultiConnectTest_ConnectionCount, "count", "Number of simultanious connections. (default: 4)", false ),
2014 CLI_OPTION_END()
2015 };
2016 #endif // MDNSRESPONDER_PROJECT
2017
2018 #if( TARGET_OS_DARWIN )
2019 static void KeepAliveTestCmd( void );
2020
2021 static const char * gKeepAliveTest_OutputFormat = kOutputFormatStr_JSON;
2022 static const char * gKeepAliveTest_OutputFilePath = NULL;
2023
2024 static CLIOption kKeepAliveTestOpts[] =
2025 {
2026 CLI_OPTION_GROUP( "Results" ),
2027 FormatOption( 'f', "format", &gKeepAliveTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
2028 StringOption( 'o', "output", &gKeepAliveTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
2029
2030 TestExitStatusSection(),
2031 CLI_OPTION_END()
2032 };
2033 #endif // TARGET_OS_DARWIN
2034
2035 static void DNSSECTestCmd( void );
2036
2037 static const char * gDNSSECTest_TestCaseName = NULL;
2038 #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
2039 static const char * gDNSSECTest_OutputFormat = kOutputFormatStr_JSON;
2040 static const char * gDNSSECTest_OutputFilePath = NULL;
2041 #endif
2042
2043 static CLIOption kDNSSECTestOpts[] =
2044 {
2045 StringOption( 'n', "testCaseName", &gDNSSECTest_TestCaseName, "Specifies the DNSSEC test that the user intends to run", "test name", true ),
2046
2047 CLI_OPTION_GROUP( "Results" ),
2048 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
2049 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
2050
2051 TestExitStatusSection(),
2052 CLI_OPTION_END()
2053 };
2054
2055 static CLIOption kTestOpts[] =
2056 {
2057 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
2058 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts, "Tests mDNS service discovery for correctness.", false ),
2059 Command( "dotlocal", DotLocalTestCmd, kDotLocalTestOpts, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
2060 Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts, "Tests various probing conflict scenarios.", false ),
2061 Command( "registration", RegistrationTestCmd, kRegistrationTestOpts, "Tests service registrations.", false ),
2062 #if( MDNSRESPONDER_PROJECT )
2063 Command( "fallback", FallbackTestCmd, kFallbackTestOpts, "Tests DNS server fallback.", false ),
2064 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd, kExpensiveConstrainedTestOpts, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
2065 Command( "dnsproxy", DNSProxyTestCmd, kDNSProxyTestOpts, "Tests mDNSResponder's DNS proxy.", false ),
2066 Command( "rcodes", RCodeTestCmd, kRCodeTestOpts, "Tests handling of all DNS RCODEs.", false ),
2067 Command( "xctest", XCTestCmd, kXCTestOpts, "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
2068 Command( "multiconnect", MultiConnectTestCmd, kMultiConnectTestOpts, "Tests multiple simultanious connections.", false ),
2069 #endif
2070 #if( TARGET_OS_DARWIN )
2071 Command( "keepalive", KeepAliveTestCmd, kKeepAliveTestOpts, "Tests keepalive record registrations.", false ),
2072 #endif
2073 Command( "dnssec", DNSSECTestCmd, kDNSSECTestOpts, "Tests mDNSResponder's DNSSEC validation", false),
2074 CLI_OPTION_END()
2075 };
2076
2077 //===========================================================================================================================
2078 // SSDP Command Options
2079 //===========================================================================================================================
2080
2081 static int gSSDPDiscover_MX = 1;
2082 static const char * gSSDPDiscover_ST = "ssdp:all";
2083 static int gSSDPDiscover_ReceiveSecs = 1;
2084 static int gSSDPDiscover_UseIPv4 = false;
2085 static int gSSDPDiscover_UseIPv6 = false;
2086 static int gSSDPDiscover_Verbose = false;
2087
2088 static CLIOption kSSDPDiscoverOpts[] =
2089 {
2090 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
2091 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
2092 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
2093 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
2094 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
2095 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
2096 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
2097 CLI_OPTION_END()
2098 };
2099
2100 static void SSDPDiscoverCmd( void );
2101
2102 static CLIOption kSSDPOpts[] =
2103 {
2104 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
2105 CLI_OPTION_END()
2106 };
2107
2108 #if( TARGET_OS_DARWIN )
2109 //===========================================================================================================================
2110 // res_query Command Options
2111 //===========================================================================================================================
2112
2113 static void ResQueryCmd( void );
2114
2115 static const char * gResQuery_Name = NULL;
2116 static const char * gResQuery_Type = NULL;
2117 static const char * gResQuery_Class = NULL;
2118 static int gResQuery_UseLibInfo = false;
2119
2120 static CLIOption kResQueryOpts[] =
2121 {
2122 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
2123 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
2124 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
2125 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
2126 CLI_OPTION_END()
2127 };
2128
2129 //===========================================================================================================================
2130 // dns_query Command Options
2131 //===========================================================================================================================
2132
2133 static void ResolvDNSQueryCmd( void );
2134
2135 static const char * gResolvDNSQuery_Name = NULL;
2136 static const char * gResolvDNSQuery_Type = NULL;
2137 static const char * gResolvDNSQuery_Class = NULL;
2138 static const char * gResolvDNSQuery_Path = NULL;
2139
2140 static CLIOption kResolvDNSQueryOpts[] =
2141 {
2142 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
2143 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
2144 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
2145 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
2146 CLI_OPTION_END()
2147 };
2148
2149 //===========================================================================================================================
2150 // CFHost Command Options
2151 //===========================================================================================================================
2152
2153 static void CFHostCmd( void );
2154
2155 static const char * gCFHost_Name = NULL;
2156 static int gCFHost_WaitSecs = 0;
2157
2158 static CLIOption kCFHostOpts[] =
2159 {
2160 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
2161 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
2162 CLI_OPTION_END()
2163 };
2164
2165 static CLIOption kLegacyOpts[] =
2166 {
2167 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
2168 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
2169 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
2170 CLI_OPTION_END()
2171 };
2172
2173 //===========================================================================================================================
2174 // DNSConfigAdd Command Options
2175 //===========================================================================================================================
2176
2177 static void DNSConfigAddCmd( void );
2178
2179 static CFStringRef gDNSConfigAdd_ID = NULL;
2180 static char ** gDNSConfigAdd_IPAddrArray = NULL;
2181 static size_t gDNSConfigAdd_IPAddrCount = 0;
2182 static char ** gDNSConfigAdd_DomainArray = NULL;
2183 static size_t gDNSConfigAdd_DomainCount = 0;
2184 static const char * gDNSConfigAdd_Interface = NULL;
2185 static int gDNSConfigAdd_SearchOrder = -1;
2186
2187 static CLIOption kDNSConfigAddOpts[] =
2188 {
2189 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
2190 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
2191 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
2192 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
2193 IntegerOption( 'o', "searchOrder", &gDNSConfigAdd_SearchOrder, "integer", "Resolver entry's search order. Will only be set for values >= 0. (default: -1)", false ),
2194
2195 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
2196 CLI_OPTION_END()
2197 };
2198
2199 //===========================================================================================================================
2200 // DNSConfigRemove Command Options
2201 //===========================================================================================================================
2202
2203 static void DNSConfigRemoveCmd( void );
2204
2205 static CFStringRef gDNSConfigRemove_ID = NULL;
2206
2207 static CLIOption kDNSConfigRemoveOpts[] =
2208 {
2209 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
2210
2211 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
2212 CLI_OPTION_END()
2213 };
2214
2215 static CLIOption kDNSConfigOpts[] =
2216 {
2217 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2218 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2219 CLI_OPTION_END()
2220 };
2221
2222 //===========================================================================================================================
2223 // XPCSend
2224 //===========================================================================================================================
2225
2226 static void XPCSendCmd( void );
2227
2228 static const char * gXPCSend_ServiceName = NULL;
2229 static const char * gXPCSend_MessageStr = NULL;
2230
2231 static const char kXPCSendMessageSection_Name[] = "Message Argument";
2232 static const char kXPCSendMessageSection_Text[] =
2233 "XPC messages are described as a string using the following syntax.\n"
2234 "\n"
2235 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
2236 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
2237 "currently supported XPC types.\n"
2238 "\n"
2239 "Arrays begin with a '[' and end with a ']'.\n"
2240 "\n"
2241 "The following non-container XPC types are supported:\n"
2242 "\n"
2243 "Type Syntax Example\n"
2244 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
2245 "data data:<hex string> data:C0000201\n"
2246 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
2247 "string string:<string> string:hello\\ world\n"
2248 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
2249 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
2250 "\n"
2251 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
2252 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
2253
2254 static CLIOption kXPCSendOpts[] =
2255 {
2256 StringOption( 's', "service", &gXPCSend_ServiceName, "service name", "XPC service name.", true ),
2257 StringOption( 'm', "message", &gXPCSend_MessageStr, "message", "XPC message as a string.", true ),
2258
2259 CLI_SECTION( kXPCSendMessageSection_Name, kXPCSendMessageSection_Text ),
2260 CLI_OPTION_END()
2261 };
2262 #endif // TARGET_OS_DARWIN
2263
2264 #if( MDNSRESPONDER_PROJECT )
2265 //===========================================================================================================================
2266 // InterfaceMonitor Command Options
2267 //===========================================================================================================================
2268
2269 static void InterfaceMonitorCmd( void );
2270
2271 static CLIOption kInterfaceMonitorOpts[] =
2272 {
2273 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
2274 CLI_OPTION_END()
2275 };
2276
2277 //===========================================================================================================================
2278 // Querier Command Options
2279 //===========================================================================================================================
2280
2281 #define kMDNSResolverTypeStr_Normal "normal"
2282 #define kMDNSResolverTypeStr_TCPOnly "tcp"
2283 #define kMDNSResolverTypeStr_TLS "tls"
2284 #define kMDNSResolverTypeStr_HTTPS "https"
2285
2286 static const char * gQuerier_Name = NULL;
2287 static const char * gQuerier_Type = "A";
2288 static const char * gQuerier_Class = "IN";
2289 static const char * gQuerier_Delegator = NULL;
2290 static int gQuerier_DNSSECOK = false;
2291 static int gQuerier_CheckingDisabled = false;
2292 static const char * gQuerier_ResolverType = NULL;
2293 static char ** gQuerier_ServerAddrs = NULL;
2294 static size_t gQuerier_ServerAddrCount = 0;
2295 static const char * gQuerier_ProviderName = NULL;
2296 static const char * gQuerier_URLPath = NULL;
2297 static int gQuerier_NoConnectionReuse = false;
2298 static int gQuerier_SquashCNAMEs = false;
2299
2300 static CLIOption kQuerierOpts[] =
2301 {
2302 StringOption( 'i', "interface", &gInterface, "name or index", "If specified, network traffic is scoped to this interface.", false ),
2303 StringOption( 'n', "name", &gQuerier_Name, "name", "Question name (QNAME).", true ),
2304 StringOption( 't', "type", &gQuerier_Type, "type", "Question type (QTYPE). (default: A)", false ),
2305 StringOption( 'c', "class", &gQuerier_Class, "class", "Question class (QCLASS). (default: IN)", false ),
2306 StringOption( 0 , "delegator", &gQuerier_Delegator, "PID|UUID", "Delegator's PID or UUID.", false ),
2307 BooleanOption( 0 , "dnssec", &gQuerier_DNSSECOK, "Have queries include an OPT record with the DNSSEC OK (DO) bit set." ),
2308 BooleanOption( 0 , "checkingDisabled", &gQuerier_CheckingDisabled, "Set the Checking Disabled (CD) bit in queries." ),
2309
2310 CLI_OPTION_GROUP( "Resolver-Specific Options" ),
2311 StringOptionEx( 'r', "resolverType", &gQuerier_ResolverType, "resolver type", "Specifies the type of resolver to use.", false,
2312 "\n"
2313 "Use '" kMDNSResolverTypeStr_Normal "' for DNS over UDP and TCP.\n"
2314 "Use '" kMDNSResolverTypeStr_TCPOnly "' for DNS over TCP.\n"
2315 "Use '" kMDNSResolverTypeStr_TLS "' for DNS over TLS.\n"
2316 "Use '" kMDNSResolverTypeStr_HTTPS "' for DNS over HTTPS.\n"
2317 "\n"
2318 "If no resolver type is specified, an mdns_dns_service_manager will be used to determine which DNS service to use\n"
2319 "based on the system's DNS settings. In this case, the other resolver-specific options will be ignored.\n"
2320 "\n"
2321 ),
2322 MultiStringOptionEx( 's', "server", &gQuerier_ServerAddrs, &gQuerier_ServerAddrCount, "IP address", "Server's IPv4 or IPv6 address with optionally-specified port.", false,
2323 "\n"
2324 "Use this option one or more times to specify a DNS service's server(s) by IP address.\n"
2325 "\n"
2326 "If no server IP addresses are specified for DNS over TLS/HTTPS resolvers, then connections to the DNS service\n"
2327 "will use the specified provider name as the DNS service's hostname.\n"
2328 "\n"
2329 ),
2330 StringOption( 'p', "providerName", &gQuerier_ProviderName, "domain name", "Provider's domain name for DNS over TLS/HTTPS.", false ),
2331 StringOption( 'q', "urlPath", &gQuerier_URLPath, "path", "URL path for DNS over HTTPS.", false ),
2332 BooleanOption( 0 , "noConnectionReuse", &gQuerier_NoConnectionReuse, "Disable connection reuse." ),
2333 BooleanOption( 0 , "squashCNAMEs", &gQuerier_SquashCNAMEs, "Squash CNAME chains in responses." ),
2334 CLI_OPTION_END()
2335 };
2336
2337 static void QuerierCommand( void );
2338
2339 //===========================================================================================================================
2340 // DNSProxy Command Options
2341 //===========================================================================================================================
2342
2343 static void DNSProxyCmd( void );
2344
2345 static char ** gDNSProxy_InputInterfaces = NULL;
2346 static size_t gDNSProxy_InputInterfaceCount = 0;
2347 static const char * gDNSProxy_OutputInterface = NULL;
2348 static const char * gDNSProxy_DNS64IPv6Prefix = NULL;
2349
2350 static CLIOption kDNSProxyOpts[] =
2351 {
2352 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces, &gDNSProxy_InputInterfaceCount, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2353 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2354 StringOption( 'p', "dns64Prefix", &gDNSProxy_DNS64IPv6Prefix, "IPv6 prefix", "IPv6 prefix to use for DNS64 AAAA record synthesis.", false ),
2355 CLI_OPTION_END()
2356 };
2357
2358 //===========================================================================================================================
2359 // GetAddrInfoNew Command Options
2360 //===========================================================================================================================
2361
2362 static const char * gGAINew_Hostname = NULL;
2363 static const char * gGAINew_DelegatorID = NULL;
2364 static const char * gGAINew_ServiceScheme = NULL;
2365 static const char * gGAINew_AccountID = NULL;
2366 static int gGAINew_ProtocolIPv4 = false;
2367 static int gGAINew_ProtocolIPv6 = false;
2368 static int gGAINew_WantAuthTags = false;
2369 static int gGAINew_OneShot = false;
2370 static int gGAINew_TimeLimitSecs = 0;
2371 static const char * gGAINew_QoS = NULL;
2372
2373 #define kQoSTypeStr_Unspecified "unspecified"
2374 #define kQoSTypeStr_Background "background"
2375 #define kQoSTypeStr_Utility "utility"
2376 #define kQoSTypeStr_Default "default"
2377 #define kQoSTypeStr_UserInitiated "userInitiated"
2378 #define kQoSTypeStr_UserInteractive "userInteractive"
2379
2380 #define kQoSArgShortName "QoS class"
2381
2382 static CLIOption kGetAddrInfoNewOpts[] =
2383 {
2384 InterfaceOption(),
2385 StringOption( 'n', "name", &gGAINew_Hostname, "domain name", "Hostname to resolve.", true ),
2386 StringOption( 'd', "delegate", &gGAINew_DelegatorID, "PID|UUID", "Delegator's PID or UUID. If PID p < 0, the audit token of PID |p| will be used.", false ),
2387 StringOption( 0, "accountID", &gGAINew_AccountID, "account ID", "Account ID string.", false ),
2388 StringOption( 0, "serviceScheme", &gGAINew_ServiceScheme, "scheme", "Service scheme such as '_443._https'.", false ),
2389 BooleanOption( 0 , "ipv4", &gGAINew_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
2390 BooleanOption( 0 , "ipv6", &gGAINew_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
2391 BooleanOption( 'a', "wantAuthTags", &gGAINew_WantAuthTags, "Want authentication tags." ),
2392
2393 CLI_OPTION_GROUP( "Flags" ),
2394 DNSSDFlagsOption(),
2395 DNSSDFlagsOption_AllowExpiredAnswers(),
2396 DNSSDFlagsOption_DenyCellular(),
2397 DNSSDFlagsOption_DenyConstrained(),
2398 DNSSDFlagsOption_DenyExpensive(),
2399 DNSSDFlagsOption_IncludeAWDL(),
2400 DNSSDFlagsOption_PathEvalDone(),
2401 DNSSDFlagsOption_ReturnIntermediates(),
2402 DNSSDFlagsOption_SuppressUnusable(),
2403 DNSSDFlagsOption_Timeout(),
2404
2405 CLI_OPTION_GROUP( "Operation" ),
2406 ConnectionOptions(),
2407 BooleanOption( 'o', "oneshot", &gGAINew_OneShot, "Finish after first set of results." ),
2408 IntegerOption( 'l', "timeLimit", &gGAINew_TimeLimitSecs, "seconds", "Time limit for dnssd_getaddrinfo operation. Use '0' for no limit. (default: 0)", false ),
2409 StringOptionEx( 'q', "qos", &gGAINew_QoS, kQoSArgShortName, "Specifies the QoS of the queue used for the dnssd_getaddrinfo object.", false,
2410 "\n"
2411 "Use '" kQoSTypeStr_Unspecified "' for QOS_CLASS_UNSPECIFIED.\n"
2412 "Use '" kQoSTypeStr_Background "' for QOS_CLASS_BACKGROUND.\n"
2413 "Use '" kQoSTypeStr_Utility "' for QOS_CLASS_UTILITY.\n"
2414 "Use '" kQoSTypeStr_Default "' for QOS_CLASS_DEFAULT.\n"
2415 "Use '" kQoSTypeStr_UserInitiated "' for QOS_CLASS_USER_INITIATED.\n"
2416 "Use '" kQoSTypeStr_UserInteractive "' for QOS_CLASS_USER_INTERACTIVE.\n"
2417 "\n"
2418 ),
2419 CLI_OPTION_END()
2420 };
2421
2422 static void GetAddrInfoNewCommand( void );
2423
2424 #endif // MDNSRESPONDER_PROJECT
2425
2426 //===========================================================================================================================
2427 // Command Table
2428 //===========================================================================================================================
2429
2430 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2431
2432 static void BrowseCmd( void );
2433 static void GetAddrInfoCmd( void );
2434 static void QueryRecordCmd( void );
2435 static void RegisterCmd( void );
2436 static void RegisterRecordCmd( void );
2437 static void ResolveCmd( void );
2438 static void ReconfirmCmd( void );
2439 static void GetAddrInfoPOSIXCmd( void );
2440 static void ReverseLookupCmd( void );
2441 static void PortMappingCmd( void );
2442 static void BrowseAllCmd( void );
2443 static void GetAddrInfoStressCmd( void );
2444 static void DNSQueryCmd( void );
2445 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2446 static void DNSCryptCmd( void );
2447 #endif
2448 static void MDNSQueryCmd( void );
2449 #if( TARGET_OS_DARWIN )
2450 static void PIDToUUIDCmd( void );
2451 #endif
2452 static void DaemonVersionCmd( void );
2453
2454 static CLIOption kGlobalOpts[] =
2455 {
2456 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2457 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2458 CLI_OPTION_HELP(),
2459
2460 // Common commands.
2461
2462 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2463 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2464 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2465 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2466 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2467 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2468 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2469 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2470 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2471 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2472 #if( TARGET_OS_DARWIN )
2473 Command( "registerKA", RegisterKACmd, kRegisterKA_Opts, "Uses DNSServiceSleepKeepalive_sockaddr() to register a keep alive record.", false ),
2474 #endif
2475 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2476
2477 // Uncommon commands.
2478
2479 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2480 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2481 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2482 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2483 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2484 #endif
2485 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2486 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2487 #if( TARGET_OS_DARWIN )
2488 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2489 #endif
2490 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2491 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2492 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2493 Command( "ssdp", NULL, kSSDPOpts, "Simple Service Discovery Protocol (SSDP).", true ),
2494 #if( TARGET_OS_DARWIN )
2495 Command( "legacy", NULL, kLegacyOpts, "Legacy DNS API.", true ),
2496 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2497 Command( "xpcsend", XPCSendCmd, kXPCSendOpts, "Sends a message to an XPC service.", true ),
2498 #endif
2499 #if( MDNSRESPONDER_PROJECT )
2500 Command( "interfaceMonitor", InterfaceMonitorCmd, kInterfaceMonitorOpts, "Instantiates an mdns_interface_monitor.", true ),
2501 Command( "querier", QuerierCommand, kQuerierOpts, "Sends a DNS query using mdns_querier.", true ),
2502 Command( "dnsproxy", DNSProxyCmd, kDNSProxyOpts, "Enables mDNSResponder's DNS proxy.", true ),
2503 Command( "getaddrinfo-new", GetAddrInfoNewCommand, kGetAddrInfoNewOpts, "Uses dnssd_getaddrinfo to resolve a hostname to IP addresses.", false ),
2504 #endif
2505 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2506
2507 CLI_COMMAND_HELP(),
2508 CLI_OPTION_END()
2509 };
2510
2511 //===========================================================================================================================
2512 // Helper Prototypes
2513 //===========================================================================================================================
2514
2515 #define kExitReason_OneShotDone "one-shot done"
2516 #define kExitReason_ReceivedResponse "received response"
2517 #define kExitReason_SIGINT "interrupt signal"
2518 #define kExitReason_Timeout "timeout"
2519 #define kExitReason_TimeLimit "time limit"
2520
2521 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2522
2523 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2524
2525 typedef enum
2526 {
2527 kConnectionType_None = 0,
2528 kConnectionType_Normal = 1,
2529 kConnectionType_DelegatePID = 2,
2530 kConnectionType_DelegateUUID = 3
2531
2532 } ConnectionType;
2533
2534 typedef struct
2535 {
2536 ConnectionType type;
2537 union
2538 {
2539 int32_t pid;
2540 uint8_t uuid[ 16 ];
2541
2542 } delegate;
2543
2544 } ConnectionDesc;
2545
2546 static OSStatus
2547 CreateConnectionFromArgString(
2548 const char * inString,
2549 dispatch_queue_t inQueue,
2550 DNSServiceRef * outSDRef,
2551 ConnectionDesc * outDesc );
2552 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2553 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2554 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2555 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2556
2557 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2558
2559 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2560 static const char * RecordTypeToString( int inValue );
2561 #if( MDNSRESPONDER_PROJECT )
2562 static const char * RecordClassToString( int inValue );
2563 #endif
2564
2565 static OSStatus
2566 WriteDNSQueryMessage(
2567 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2568 uint16_t inMsgID,
2569 uint16_t inFlags,
2570 const char * inQName,
2571 uint16_t inQType,
2572 uint16_t inQClass,
2573 size_t * outMsgLen );
2574
2575 // Dispatch helpers
2576
2577 typedef void ( *DispatchHandler )( void *inContext );
2578
2579 static OSStatus
2580 DispatchSignalSourceCreate(
2581 int inSignal,
2582 dispatch_queue_t inQueue,
2583 DispatchHandler inEventHandler,
2584 void * inContext,
2585 dispatch_source_t * outSource );
2586 static OSStatus
2587 DispatchSocketSourceCreate(
2588 SocketRef inSock,
2589 dispatch_source_type_t inType,
2590 dispatch_queue_t inQueue,
2591 DispatchHandler inEventHandler,
2592 DispatchHandler inCancelHandler,
2593 void * inContext,
2594 dispatch_source_t * outSource );
2595
2596 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2597 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2598
2599 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2600 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2601
2602 static OSStatus
2603 DispatchTimerCreate(
2604 dispatch_time_t inStart,
2605 uint64_t inIntervalNs,
2606 uint64_t inLeewayNs,
2607 dispatch_queue_t inQueue,
2608 DispatchHandler inEventHandler,
2609 DispatchHandler inCancelHandler,
2610 void * inContext,
2611 dispatch_source_t * outTimer );
2612
2613 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2614 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2615
2616 #if( TARGET_OS_DARWIN )
2617 static OSStatus
2618 DispatchProcessMonitorCreate(
2619 pid_t inPID,
2620 unsigned long inFlags,
2621 dispatch_queue_t inQueue,
2622 DispatchHandler inEventHandler,
2623 DispatchHandler inCancelHandler,
2624 void * inContext,
2625 dispatch_source_t * outMonitor );
2626 #endif
2627
2628 static const char * ServiceTypeDescription( const char *inName );
2629
2630 typedef void ( *SocketContextFinalizer_f )( void *inUserCtx );
2631
2632 typedef struct
2633 {
2634 SocketRef sock; // Socket.
2635 int32_t refCount; // Reference count.
2636 void * userContext; // User's context.
2637 SocketContextFinalizer_f userFinalizer; // User's finalizer.
2638
2639 } SocketContext;
2640
2641 static SocketContext * SocketContextCreate( SocketRef inSock, void *inUserContext, OSStatus *outError );
2642 static SocketContext *
2643 SocketContextCreateEx(
2644 SocketRef inSock,
2645 void * inUserContext,
2646 SocketContextFinalizer_f inUserFinalizer,
2647 OSStatus * outError );
2648 static SocketContext * SocketContextRetain( SocketContext *inContext );
2649 static void SocketContextRelease( SocketContext *inContext );
2650 static void SocketContextCancelHandler( void *inContext );
2651 static void SocketContextFinalizerCF( void *inUserCtx );
2652
2653 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2654
2655 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2656 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2657 #if( TARGET_OS_DARWIN )
2658 static int64_t _StringToInt64( const char *inString, OSStatus *outError );
2659 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError );
2660 static pid_t _StringToPID( const char *inString, OSStatus *outError );
2661 static OSStatus
2662 _ParseEscapedString(
2663 const char * inSrc,
2664 const char * inEnd,
2665 const char * inDelimiters,
2666 char * inBufPtr,
2667 size_t inBufLen,
2668 size_t * outCopiedLen,
2669 size_t * outActualLen,
2670 const char ** outPtr );
2671 #endif
2672 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2673 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2674 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2675 #if( TARGET_OS_DARWIN )
2676 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2677 #endif
2678 static OSStatus
2679 _ServerSocketOpenEx2(
2680 int inFamily,
2681 int inType,
2682 int inProtocol,
2683 const void * inAddr,
2684 int inPort,
2685 int * outPort,
2686 int inRcvBufSize,
2687 Boolean inNoPortReuse,
2688 SocketRef * outSock );
2689
2690 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2691 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2692
2693 static OSStatus
2694 CreateMulticastSocket(
2695 const struct sockaddr * inAddr,
2696 int inPort,
2697 const char * inIfName,
2698 uint32_t inIfIndex,
2699 Boolean inJoin,
2700 int * outPort,
2701 SocketRef * outSock );
2702
2703 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2704 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2705 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2706 static OSStatus CheckRootUser( void );
2707 #if( TARGET_OS_POSIX )
2708 static OSStatus
2709 _SpawnCommand(
2710 pid_t * outPID,
2711 const char * inStdOutRedirect,
2712 const char * inStdErrRedirect,
2713 const char * inFormat,
2714 ... );
2715 #endif
2716 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2717 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2718 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2719 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2720 static OSStatus
2721 CreateNSECRecordData(
2722 const uint8_t * inNextDomainName,
2723 uint8_t ** outPtr,
2724 size_t * outLen,
2725 unsigned int inTypeCount,
2726 ... );
2727 static OSStatus
2728 AppendSOARecord(
2729 DataBuffer * inDB,
2730 const uint8_t * inNamePtr,
2731 size_t inNameLen,
2732 uint16_t inType,
2733 uint16_t inClass,
2734 uint32_t inTTL,
2735 const uint8_t * inMName,
2736 const uint8_t * inRName,
2737 uint32_t inSerial,
2738 uint32_t inRefresh,
2739 uint32_t inRetry,
2740 uint32_t inExpire,
2741 uint32_t inMinimumTTL,
2742 size_t * outLen );
2743 static OSStatus
2744 CreateSOARecordData(
2745 const uint8_t * inMName,
2746 const uint8_t * inRName,
2747 uint32_t inSerial,
2748 uint32_t inRefresh,
2749 uint32_t inRetry,
2750 uint32_t inExpire,
2751 uint32_t inMinimumTTL,
2752 uint8_t ** outPtr,
2753 size_t * outLen );
2754 static OSStatus
2755 _DataBuffer_AppendDNSQuestion(
2756 DataBuffer * inDB,
2757 const uint8_t * inNamePtr,
2758 size_t inNameLen,
2759 uint16_t inType,
2760 uint16_t inClass );
2761 static OSStatus
2762 _DataBuffer_AppendDNSRecord(
2763 DataBuffer * inDB,
2764 const uint8_t * inNamePtr,
2765 size_t inNameLen,
2766 uint16_t inType,
2767 uint16_t inClass,
2768 uint32_t inTTL,
2769 const uint8_t * inRDataPtr,
2770 size_t inRDataLen );
2771 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2772
2773 typedef struct MDNSInterfaceItem MDNSInterfaceItem;
2774 struct MDNSInterfaceItem
2775 {
2776 MDNSInterfaceItem * next;
2777 char * ifName;
2778 uint32_t ifIndex;
2779 Boolean hasIPv4;
2780 Boolean hasIPv6;
2781 Boolean isAWDL;
2782 Boolean isWiFi;
2783 };
2784
2785 typedef enum
2786 {
2787 kMDNSInterfaceSubset_All = 0, // All mDNS-capable interfaces.
2788 kMDNSInterfaceSubset_AWDL = 1, // All mDNS-capable AWDL interfaces.
2789 kMDNSInterfaceSubset_NonAWDL = 2 // All mDNS-capable non-AWDL iterfaces.
2790
2791 } MDNSInterfaceSubset;
2792
2793 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2794 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2795 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2796 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2797
2798 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2799 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName );
2800 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName );
2801 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2802 #if( TARGET_OS_DARWIN )
2803 static OSStatus _InterfaceIPv6AddressAdd( const char *inIfName, uint8_t inAddr[ STATIC_PARAM 16 ], int inMaskBitLen );
2804 static OSStatus _InterfaceIPv6AddressRemove( const char *inIfName, const uint8_t inAddr[ STATIC_PARAM 16 ] );
2805 #endif
2806 static int64_t _TicksDiff( uint64_t inT1, uint64_t inT2 );
2807 static void _SockAddrInitIPv4( struct sockaddr_in *inSA, uint32_t inIPv4, uint16_t inPort );
2808 static void
2809 _SockAddrInitIPv6(
2810 struct sockaddr_in6 * inSA,
2811 const uint8_t inIPv6[ STATIC_PARAM 16 ],
2812 uint32_t inScope,
2813 uint16_t inPort );
2814
2815 #define kIP6ArpaDomainStr "ip6.arpa."
2816 #define kReverseIPv6DomainNameBufLen ( ( 4 * 16 ) + sizeof_string( kIP6ArpaDomainStr ) + 1 )
2817
2818 static void
2819 _WriteReverseIPv6DomainNameString(
2820 const uint8_t inIPv6Addr[ STATIC_PARAM 16 ],
2821 char outBuffer[ STATIC_PARAM kReverseIPv6DomainNameBufLen ] );
2822
2823 #define kInAddrArpaDomainStr "in-addr.arpa."
2824 #define kReverseIPv4DomainNameBufLen ( ( 4 * 4 ) + sizeof_string( kInAddrArpaDomainStr ) + 1 )
2825
2826 static void
2827 _WriteReverseIPv4DomainNameString(
2828 uint32_t inIPv4Addr,
2829 char outBuffer[ STATIC_PARAM kReverseIPv4DomainNameBufLen ] );
2830
2831 #if( MDNSRESPONDER_PROJECT )
2832 static OSStatus _SetDefaultFallbackDNSService( const char *inFallbackDNSServiceStr );
2833 #endif
2834
2835 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2836 static OSStatus
2837 _StringToIPv4Address(
2838 const char * inStr,
2839 StringToIPAddressFlags inFlags,
2840 uint32_t * outIP,
2841 int * outPort,
2842 uint32_t * outSubnet,
2843 uint32_t * outRouter,
2844 const char ** outStr );
2845 static void _StringArray_Free( char **inArray, size_t inCount );
2846 static OSStatus
2847 _StringToIPv6Address(
2848 const char * inStr,
2849 StringToIPAddressFlags inFlags,
2850 uint8_t outIPv6[ 16 ],
2851 uint32_t * outScope,
2852 int * outPort,
2853 int * outPrefix,
2854 const char ** outStr );
2855 static Boolean
2856 _ParseQuotedEscapedString(
2857 const char * inSrc,
2858 const char * inEnd,
2859 const char * inDelimiters,
2860 char * inBuf,
2861 size_t inMaxLen,
2862 size_t * outCopiedLen,
2863 size_t * outTotalLen,
2864 const char ** outSrc );
2865 static void * _memdup( const void *inPtr, size_t inLen );
2866 static int _memicmp( const void *inP1, const void *inP2, size_t inLen );
2867 static uint32_t _FNV1( const void *inData, size_t inSize );
2868
2869 #define Unused( X ) (void)(X)
2870
2871 //===========================================================================================================================
2872 // MDNSCollider
2873 //===========================================================================================================================
2874
2875 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2876
2877 typedef uint32_t MDNSColliderProtocols;
2878 #define kMDNSColliderProtocol_None 0
2879 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2880 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2881
2882 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2883
2884 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2885 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2886 static void MDNSColliderStop( MDNSColliderRef inCollider );
2887 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2888 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2889 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2890 static void
2891 MDNSColliderSetStopHandler(
2892 MDNSColliderRef inCollider,
2893 MDNSColliderStopHandler_f inStopHandler,
2894 void * inStopContext );
2895 static OSStatus
2896 MDNSColliderSetRecord(
2897 MDNSColliderRef inCollider,
2898 const uint8_t * inName,
2899 uint16_t inType,
2900 const void * inRDataPtr,
2901 size_t inRDataLen );
2902 static CFTypeID MDNSColliderGetTypeID( void );
2903
2904 //===========================================================================================================================
2905 // ServiceBrowser
2906 //===========================================================================================================================
2907
2908 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2909 typedef struct ServiceBrowserResults ServiceBrowserResults;
2910 typedef struct SBRDomain SBRDomain;
2911 typedef struct SBRServiceType SBRServiceType;
2912 typedef struct SBRServiceInstance SBRServiceInstance;
2913 typedef struct SBRIPAddress SBRIPAddress;
2914
2915 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2916
2917 struct ServiceBrowserResults
2918 {
2919 SBRDomain * domainList; // List of domains in which services were found.
2920 };
2921
2922 struct SBRDomain
2923 {
2924 SBRDomain * next; // Next domain in list.
2925 char * name; // Name of domain represented by this object.
2926 SBRServiceType * typeList; // List of service types in this domain.
2927 };
2928
2929 struct SBRServiceType
2930 {
2931 SBRServiceType * next; // Next service type in list.
2932 char * name; // Name of service type represented by this object.
2933 SBRServiceInstance * instanceList; // List of service instances of this service type.
2934 };
2935
2936 struct SBRServiceInstance
2937 {
2938 SBRServiceInstance * next; // Next service instance in list.
2939 char * name; // Name of service instance represented by this object.
2940 char * hostname; // Target from service instance's SRV record.
2941 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2942 uint16_t port; // Port from service instance's SRV record.
2943 uint8_t * txtPtr; // Service instance's TXT record data.
2944 size_t txtLen; // Service instance's TXT record data length.
2945 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2946 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2947 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2948 };
2949
2950 struct SBRIPAddress
2951 {
2952 SBRIPAddress * next; // Next IP address in list.
2953 sockaddr_ip sip; // IPv4 or IPv6 address.
2954 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2955 };
2956
2957 static CFTypeID ServiceBrowserGetTypeID( void );
2958 static OSStatus
2959 ServiceBrowserCreate(
2960 dispatch_queue_t inQueue,
2961 uint32_t inInterfaceIndex,
2962 const char * inDomain,
2963 unsigned int inBrowseTimeSecs,
2964 Boolean inIncludeAWDL,
2965 ServiceBrowserRef * outBrowser );
2966 #if( MDNSRESPONDER_PROJECT )
2967 static void ServiceBrowserSetUseNewGAI( ServiceBrowserRef inBrowser, Boolean inUseNewGAI );
2968 #endif
2969 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2970 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2971 static void
2972 ServiceBrowserSetCallback(
2973 ServiceBrowserRef inBrowser,
2974 ServiceBrowserCallback_f inCallback,
2975 void * inContext );
2976 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2977 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2978
2979 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2980
2981 //===========================================================================================================================
2982 // DNSServer
2983 //===========================================================================================================================
2984
2985 typedef struct DNSServerPrivate * DNSServerRef;
2986
2987 typedef void ( *DNSServerStartHandler_f )( const sockaddr_ip *inServerArray, size_t inServerCount, void *inCtx );
2988 typedef void ( *DNSServerStopHandler_f )( OSStatus inError, void *inCtx );
2989
2990 static CFTypeID DNSServerGetTypeID( void );
2991 static OSStatus
2992 _DNSServerCreate(
2993 dispatch_queue_t inQueue,
2994 DNSServerStartHandler_f inStartHandler,
2995 DNSServerStopHandler_f inStopHandler,
2996 void * inUserContext,
2997 unsigned int inResponseDelayMs,
2998 uint32_t inDefaultTTL,
2999 const sockaddr_ip * inServerArray,
3000 size_t inServerCount,
3001 const char * inDomain,
3002 Boolean inBadUDPMode,
3003 DNSServerRef * outServer );
3004 static OSStatus _DNSServerSetIgnoredQType( DNSServerRef inServer, int inQType );
3005 static void _DNSServerStart( DNSServerRef inServer );
3006 static void _DNSServerStop( DNSServerRef inServer );
3007
3008 #define DNSServerForget( X ) ForgetCustomEx( X, _DNSServerStop, CFRelease )
3009
3010 //===========================================================================================================================
3011 // main
3012 //===========================================================================================================================
3013
3014 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
3015 static int \
3016 _PrintFExtensionHandler_ ## NAME ( \
3017 PrintFContext * inContext, \
3018 PrintFFormat * inFormat, \
3019 PrintFVAList * inArgs, \
3020 void * inUserContext )
3021
3022 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
3023 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
3024 _PRINTF_EXTENSION_HANDLER_DECLARE( RawDNSMessage );
3025 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
3026 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
3027 _PRINTF_EXTENSION_HANDLER_DECLARE( DomainName );
3028
3029 int main( int argc, const char **argv )
3030 {
3031 OSStatus err;
3032
3033 // Route DebugServices logging output to stderr.
3034
3035 dlog_control( "DebugServices:output=file;stderr" );
3036
3037 PrintFRegisterExtension( "du:time", _PrintFExtensionHandler_Timestamp, NULL );
3038 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionHandler_DNSMessage, NULL );
3039 PrintFRegisterExtension( "du:rdnsmsg", _PrintFExtensionHandler_RawDNSMessage, NULL );
3040 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionHandler_CallbackFlags, NULL );
3041 PrintFRegisterExtension( "du:rdata", _PrintFExtensionHandler_DNSRecordData, NULL );
3042 PrintFRegisterExtension( "du:dname", _PrintFExtensionHandler_DomainName, NULL );
3043 CLIInit( argc, argv );
3044 err = CLIParse( kGlobalOpts, kCLIFlags_None );
3045 if( err ) gExitCode = 1;
3046
3047 return( gExitCode );
3048 }
3049
3050 //===========================================================================================================================
3051 // VersionOptionCallback
3052 //===========================================================================================================================
3053
3054 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
3055 {
3056 const char * srcVers;
3057 #if( MDNSRESPONDER_PROJECT )
3058 char srcStr[ 16 ];
3059 #endif
3060
3061 Unused( inOption );
3062 Unused( inArg );
3063 Unused( inUnset );
3064
3065 #if( MDNSRESPONDER_PROJECT )
3066 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
3067 #else
3068 srcVers = DNSSDUTIL_SOURCE_VERSION;
3069 #endif
3070 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
3071
3072 return( kEndingErr );
3073 }
3074
3075 //===========================================================================================================================
3076 // BrowseCmd
3077 //===========================================================================================================================
3078
3079 typedef struct BrowseResolveOp BrowseResolveOp;
3080
3081 struct BrowseResolveOp
3082 {
3083 BrowseResolveOp * next; // Next resolve operation in list.
3084 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
3085 char * fullName; // Full name of the service to resolve.
3086 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
3087 };
3088
3089 typedef struct
3090 {
3091 DNSServiceRef mainRef; // Main sdRef for shared connection.
3092 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
3093 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
3094 const char * domain; // Domain for DNSServiceBrowse operation(s).
3095 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
3096 char ** serviceTypes; // Array of service types to browse for.
3097 size_t serviceTypesCount; // Count of array of service types to browse for.
3098 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
3099 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
3100 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
3101 Boolean printedHeader; // True if results header has been printed.
3102 Boolean doResolve; // True if service instances are to be resolved.
3103 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
3104
3105 } BrowseContext;
3106
3107 static void BrowsePrintPrologue( const BrowseContext *inContext );
3108 static void BrowseContextFree( BrowseContext *inContext );
3109 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
3110 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
3111 static void DNSSD_API
3112 BrowseCallback(
3113 DNSServiceRef inSDRef,
3114 DNSServiceFlags inFlags,
3115 uint32_t inInterfaceIndex,
3116 DNSServiceErrorType inError,
3117 const char * inName,
3118 const char * inRegType,
3119 const char * inDomain,
3120 void * inContext );
3121 static void DNSSD_API
3122 BrowseResolveCallback(
3123 DNSServiceRef inSDRef,
3124 DNSServiceFlags inFlags,
3125 uint32_t inInterfaceIndex,
3126 DNSServiceErrorType inError,
3127 const char * inFullName,
3128 const char * inHostname,
3129 uint16_t inPort,
3130 uint16_t inTXTLen,
3131 const unsigned char * inTXTPtr,
3132 void * inContext );
3133 static void DNSSD_API
3134 BrowseQueryRecordCallback(
3135 DNSServiceRef inSDRef,
3136 DNSServiceFlags inFlags,
3137 uint32_t inInterfaceIndex,
3138 DNSServiceErrorType inError,
3139 const char * inFullName,
3140 uint16_t inType,
3141 uint16_t inClass,
3142 uint16_t inRDataLen,
3143 const void * inRDataPtr,
3144 uint32_t inTTL,
3145 void * inContext );
3146
3147 static void BrowseCmd( void )
3148 {
3149 OSStatus err;
3150 size_t i;
3151 BrowseContext * context = NULL;
3152 dispatch_source_t signalSource = NULL;
3153 int useMainConnection;
3154
3155 // Set up SIGINT handler.
3156
3157 signal( SIGINT, SIG_IGN );
3158 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3159 require_noerr( err, exit );
3160 dispatch_resume( signalSource );
3161
3162 // Create context.
3163
3164 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
3165 require_action( context, exit, err = kNoMemoryErr );
3166
3167 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
3168 require_action( context->opRefs, exit, err = kNoMemoryErr );
3169 context->opRefsCount = gBrowse_ServiceTypesCount;
3170
3171 // Check command parameters.
3172
3173 if( gBrowse_TimeLimitSecs < 0 )
3174 {
3175 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
3176 err = kParamErr;
3177 goto exit;
3178 }
3179
3180 // Create main connection.
3181
3182 if( gConnectionOpt )
3183 {
3184 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3185 require_noerr_quiet( err, exit );
3186 useMainConnection = true;
3187 }
3188 else
3189 {
3190 useMainConnection = false;
3191 }
3192
3193 // Get flags.
3194
3195 context->flags = GetDNSSDFlagsFromOpts();
3196 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3197
3198 // Get interface.
3199
3200 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3201 require_noerr_quiet( err, exit );
3202
3203 // Set remaining parameters.
3204
3205 context->serviceTypes = gBrowse_ServiceTypes;
3206 context->serviceTypesCount = gBrowse_ServiceTypesCount;
3207 context->domain = gBrowse_Domain;
3208 context->doResolve = gBrowse_DoResolve ? true : false;
3209 context->timeLimitSecs = gBrowse_TimeLimitSecs;
3210 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
3211
3212 // Print prologue.
3213
3214 BrowsePrintPrologue( context );
3215
3216 // Start operation(s).
3217
3218 for( i = 0; i < context->serviceTypesCount; ++i )
3219 {
3220 DNSServiceRef sdRef;
3221
3222 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3223 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
3224 BrowseCallback, context );
3225 require_noerr( err, exit );
3226
3227 context->opRefs[ i ] = sdRef;
3228 if( !useMainConnection )
3229 {
3230 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
3231 require_noerr( err, exit );
3232 }
3233 }
3234
3235 // Set time limit.
3236
3237 if( context->timeLimitSecs > 0 )
3238 {
3239 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3240 kExitReason_TimeLimit, Exit );
3241 }
3242 dispatch_main();
3243
3244 exit:
3245 dispatch_source_forget( &signalSource );
3246 if( context ) BrowseContextFree( context );
3247 if( err ) exit( 1 );
3248 }
3249
3250 //===========================================================================================================================
3251 // BrowsePrintPrologue
3252 //===========================================================================================================================
3253
3254 static void BrowsePrintPrologue( const BrowseContext *inContext )
3255 {
3256 const int timeLimitSecs = inContext->timeLimitSecs;
3257 const char * const * ptr = (const char **) inContext->serviceTypes;
3258 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
3259 char ifName[ kInterfaceNameBufLen ];
3260
3261 InterfaceIndexToName( inContext->ifIndex, ifName );
3262
3263 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3264 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3265 FPrintF( stdout, "Service types: %s", *ptr++ );
3266 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
3267 FPrintF( stdout, "\n" );
3268 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3269 FPrintF( stdout, "Time limit: " );
3270 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3271 else FPrintF( stdout, "∞\n" );
3272 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3273 FPrintF( stdout, "---\n" );
3274 }
3275
3276 //===========================================================================================================================
3277 // BrowseContextFree
3278 //===========================================================================================================================
3279
3280 static void BrowseContextFree( BrowseContext *inContext )
3281 {
3282 size_t i;
3283
3284 for( i = 0; i < inContext->opRefsCount; ++i )
3285 {
3286 DNSServiceForget( &inContext->opRefs[ i ] );
3287 }
3288 if( inContext->serviceTypes )
3289 {
3290 _StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
3291 inContext->serviceTypes = NULL;
3292 inContext->serviceTypesCount = 0;
3293 }
3294 DNSServiceForget( &inContext->mainRef );
3295 free( inContext );
3296 }
3297
3298 //===========================================================================================================================
3299 // BrowseResolveOpCreate
3300 //===========================================================================================================================
3301
3302 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
3303 {
3304 OSStatus err;
3305 BrowseResolveOp * resolveOp;
3306
3307 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
3308 require_action( resolveOp, exit, err = kNoMemoryErr );
3309
3310 resolveOp->fullName = strdup( inFullName );
3311 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
3312
3313 resolveOp->interfaceIndex = inInterfaceIndex;
3314
3315 *outOp = resolveOp;
3316 resolveOp = NULL;
3317 err = kNoErr;
3318
3319 exit:
3320 if( resolveOp ) BrowseResolveOpFree( resolveOp );
3321 return( err );
3322 }
3323
3324 //===========================================================================================================================
3325 // BrowseResolveOpFree
3326 //===========================================================================================================================
3327
3328 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
3329 {
3330 DNSServiceForget( &inOp->sdRef );
3331 ForgetMem( &inOp->fullName );
3332 free( inOp );
3333 }
3334
3335 //===========================================================================================================================
3336 // BrowseCallback
3337 //===========================================================================================================================
3338
3339 static void DNSSD_API
3340 BrowseCallback(
3341 DNSServiceRef inSDRef,
3342 DNSServiceFlags inFlags,
3343 uint32_t inInterfaceIndex,
3344 DNSServiceErrorType inError,
3345 const char * inName,
3346 const char * inRegType,
3347 const char * inDomain,
3348 void * inContext )
3349 {
3350 BrowseContext * const context = (BrowseContext *) inContext;
3351 OSStatus err;
3352 BrowseResolveOp * newOp = NULL;
3353 BrowseResolveOp ** p;
3354 char fullName[ kDNSServiceMaxDomainName ];
3355 struct timeval now;
3356
3357 Unused( inSDRef );
3358
3359 gettimeofday( &now, NULL );
3360
3361 err = inError;
3362 require_noerr( err, exit );
3363
3364 if( !context->printedHeader )
3365 {
3366 FPrintF( stdout, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
3367 context->printedHeader = true;
3368 }
3369 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
3370 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
3371
3372 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
3373
3374 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
3375 require_noerr( err, exit );
3376
3377 if( inFlags & kDNSServiceFlagsAdd )
3378 {
3379 DNSServiceRef sdRef;
3380 DNSServiceFlags flags;
3381
3382 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
3383 require_noerr( err, exit );
3384
3385 if( context->mainRef )
3386 {
3387 sdRef = context->mainRef;
3388 flags = kDNSServiceFlagsShareConnection;
3389 }
3390 else
3391 {
3392 flags = 0;
3393 }
3394 if( context->doResolve )
3395 {
3396 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
3397 NULL );
3398 require_noerr( err, exit );
3399 }
3400 else
3401 {
3402 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
3403 BrowseQueryRecordCallback, NULL );
3404 require_noerr( err, exit );
3405 }
3406
3407 newOp->sdRef = sdRef;
3408 if( !context->mainRef )
3409 {
3410 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
3411 require_noerr( err, exit );
3412 }
3413 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
3414 *p = newOp;
3415 newOp = NULL;
3416 }
3417 else
3418 {
3419 BrowseResolveOp * resolveOp;
3420
3421 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
3422 {
3423 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
3424 {
3425 break;
3426 }
3427 }
3428 if( resolveOp )
3429 {
3430 *p = resolveOp->next;
3431 BrowseResolveOpFree( resolveOp );
3432 }
3433 }
3434
3435 exit:
3436 if( newOp ) BrowseResolveOpFree( newOp );
3437 if( err ) exit( 1 );
3438 }
3439
3440 //===========================================================================================================================
3441 // BrowseQueryRecordCallback
3442 //===========================================================================================================================
3443
3444 static void DNSSD_API
3445 BrowseQueryRecordCallback(
3446 DNSServiceRef inSDRef,
3447 DNSServiceFlags inFlags,
3448 uint32_t inInterfaceIndex,
3449 DNSServiceErrorType inError,
3450 const char * inFullName,
3451 uint16_t inType,
3452 uint16_t inClass,
3453 uint16_t inRDataLen,
3454 const void * inRDataPtr,
3455 uint32_t inTTL,
3456 void * inContext )
3457 {
3458 OSStatus err;
3459 struct timeval now;
3460
3461 Unused( inSDRef );
3462 Unused( inClass );
3463 Unused( inTTL );
3464 Unused( inContext );
3465
3466 gettimeofday( &now, NULL );
3467
3468 err = inError;
3469 require_noerr( err, exit );
3470 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
3471
3472 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
3473 &now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
3474 (size_t) inRDataLen );
3475
3476 exit:
3477 if( err ) exit( 1 );
3478 }
3479
3480 //===========================================================================================================================
3481 // BrowseResolveCallback
3482 //===========================================================================================================================
3483
3484 static void DNSSD_API
3485 BrowseResolveCallback(
3486 DNSServiceRef inSDRef,
3487 DNSServiceFlags inFlags,
3488 uint32_t inInterfaceIndex,
3489 DNSServiceErrorType inError,
3490 const char * inFullName,
3491 const char * inHostname,
3492 uint16_t inPort,
3493 uint16_t inTXTLen,
3494 const unsigned char * inTXTPtr,
3495 void * inContext )
3496 {
3497 struct timeval now;
3498 char errorStr[ 64 ];
3499
3500 Unused( inSDRef );
3501 Unused( inFlags );
3502 Unused( inContext );
3503
3504 gettimeofday( &now, NULL );
3505
3506 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3507
3508 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3509 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3510 if( inTXTLen == 1 )
3511 {
3512 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3513 }
3514 else
3515 {
3516 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3517 }
3518 }
3519
3520 //===========================================================================================================================
3521 // GetAddrInfoCmd
3522 //===========================================================================================================================
3523
3524 typedef struct
3525 {
3526 DNSServiceRef mainRef; // Main sdRef for shared connection.
3527 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3528 const char * name; // Hostname to resolve.
3529 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3530 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3531 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3532 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3533 Boolean printedHeader; // True if the results header has been printed.
3534 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3535 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3536 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3537
3538 } GetAddrInfoContext;
3539
3540 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3541 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3542 static void DNSSD_API
3543 GetAddrInfoCallback(
3544 DNSServiceRef inSDRef,
3545 DNSServiceFlags inFlags,
3546 uint32_t inInterfaceIndex,
3547 DNSServiceErrorType inError,
3548 const char * inHostname,
3549 const struct sockaddr * inSockAddr,
3550 uint32_t inTTL,
3551 void * inContext );
3552
3553 static void GetAddrInfoCmd( void )
3554 {
3555 OSStatus err;
3556 DNSServiceRef sdRef;
3557 GetAddrInfoContext * context = NULL;
3558 dispatch_source_t signalSource = NULL;
3559 int useMainConnection;
3560
3561 // Set up SIGINT handler.
3562
3563 signal( SIGINT, SIG_IGN );
3564 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3565 require_noerr( err, exit );
3566 dispatch_resume( signalSource );
3567
3568 // Check command parameters.
3569
3570 if( gGetAddrInfo_TimeLimitSecs < 0 )
3571 {
3572 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3573 err = kParamErr;
3574 goto exit;
3575 }
3576
3577 #if( MDNSRESPONDER_PROJECT )
3578 if( gFallbackDNSService )
3579 {
3580 err = _SetDefaultFallbackDNSService( gFallbackDNSService );
3581 require_noerr_quiet( err, exit );
3582 }
3583 #endif
3584 // Create context.
3585
3586 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3587 require_action( context, exit, err = kNoMemoryErr );
3588
3589 // Create main connection.
3590
3591 if( gConnectionOpt )
3592 {
3593 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3594 require_noerr_quiet( err, exit );
3595 useMainConnection = true;
3596 }
3597 else
3598 {
3599 useMainConnection = false;
3600 }
3601
3602 // Get flags.
3603
3604 context->flags = GetDNSSDFlagsFromOpts();
3605 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3606
3607 // Get interface.
3608
3609 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3610 require_noerr_quiet( err, exit );
3611
3612 // Set remaining parameters.
3613
3614 context->name = gGetAddrInfo_Name;
3615 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3616 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3617 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3618 if( gGetAddrInfo_OneShot )
3619 {
3620 context->oneShotMode = true;
3621 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3622 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3623 }
3624
3625 // Print prologue.
3626
3627 GetAddrInfoPrintPrologue( context );
3628
3629 // Start operation.
3630
3631 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3632 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3633 GetAddrInfoCallback, context );
3634 require_noerr( err, exit );
3635
3636 context->opRef = sdRef;
3637 if( !useMainConnection )
3638 {
3639 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3640 require_noerr( err, exit );
3641 }
3642
3643 // Set time limit.
3644
3645 if( context->timeLimitSecs > 0 )
3646 {
3647 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3648 kExitReason_TimeLimit, Exit );
3649 }
3650 dispatch_main();
3651
3652 exit:
3653 dispatch_source_forget( &signalSource );
3654 if( context ) GetAddrInfoContextFree( context );
3655 if( err ) exit( 1 );
3656 }
3657
3658 //===========================================================================================================================
3659 // GetAddrInfoPrintPrologue
3660 //===========================================================================================================================
3661
3662 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3663 {
3664 const int timeLimitSecs = inContext->timeLimitSecs;
3665 char ifName[ kInterfaceNameBufLen ];
3666
3667 InterfaceIndexToName( inContext->ifIndex, ifName );
3668
3669 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3670 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3671 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3672 FPrintF( stdout, "Name: %s\n", inContext->name );
3673 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3674 FPrintF( stdout, "Time limit: " );
3675 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3676 else FPrintF( stdout, "∞\n" );
3677 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3678 FPrintF( stdout, "---\n" );
3679 }
3680
3681 //===========================================================================================================================
3682 // GetAddrInfoContextFree
3683 //===========================================================================================================================
3684
3685 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3686 {
3687 DNSServiceForget( &inContext->opRef );
3688 DNSServiceForget( &inContext->mainRef );
3689 free( inContext );
3690 }
3691
3692 //===========================================================================================================================
3693 // GetAddrInfoCallback
3694 //===========================================================================================================================
3695
3696 static void DNSSD_API
3697 GetAddrInfoCallback(
3698 DNSServiceRef inSDRef,
3699 DNSServiceFlags inFlags,
3700 uint32_t inInterfaceIndex,
3701 DNSServiceErrorType inError,
3702 const char * inHostname,
3703 const struct sockaddr * inSockAddr,
3704 uint32_t inTTL,
3705 void * inContext )
3706 {
3707 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3708 struct timeval now;
3709 OSStatus err;
3710 const char * addrStr;
3711 char addrStrBuf[ kSockAddrStringMaxSize ];
3712
3713 Unused( inSDRef );
3714
3715 gettimeofday( &now, NULL );
3716
3717 switch( inError )
3718 {
3719 case kDNSServiceErr_NoError:
3720 case kDNSServiceErr_NoSuchRecord:
3721 err = kNoErr;
3722 break;
3723
3724 case kDNSServiceErr_Timeout:
3725 Exit( kExitReason_Timeout );
3726
3727 default:
3728 err = inError;
3729 goto exit;
3730 }
3731
3732 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3733 {
3734 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3735 err = kTypeErr;
3736 goto exit;
3737 }
3738
3739 if( !inError )
3740 {
3741 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3742 require_noerr( err, exit );
3743 addrStr = addrStrBuf;
3744 }
3745 else
3746 {
3747 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3748 }
3749
3750 if( !context->printedHeader )
3751 {
3752 FPrintF( stdout, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3753 context->printedHeader = true;
3754 }
3755 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3756 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3757
3758 if( context->oneShotMode )
3759 {
3760 if( inFlags & kDNSServiceFlagsAdd )
3761 {
3762 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3763 else context->needIPv6 = false;
3764 }
3765 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3766 {
3767 Exit( kExitReason_OneShotDone );
3768 }
3769 }
3770
3771 exit:
3772 if( err ) exit( 1 );
3773 }
3774
3775 //===========================================================================================================================
3776 // QueryRecordCmd
3777 //===========================================================================================================================
3778
3779 typedef struct
3780 {
3781 DNSServiceRef mainRef; // Main sdRef for shared connection.
3782 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3783 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3784 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3785 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3786 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3787 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3788 Boolean printedHeader; // True if the results header was printed.
3789 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3790 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3791 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3792
3793 } QueryRecordContext;
3794
3795 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3796 static void QueryRecordContextFree( QueryRecordContext *inContext );
3797 static void DNSSD_API
3798 QueryRecordCallback(
3799 DNSServiceRef inSDRef,
3800 DNSServiceFlags inFlags,
3801 uint32_t inInterfaceIndex,
3802 DNSServiceErrorType inError,
3803 const char * inFullName,
3804 uint16_t inType,
3805 uint16_t inClass,
3806 uint16_t inRDataLen,
3807 const void * inRDataPtr,
3808 uint32_t inTTL,
3809 void * inContext );
3810
3811 static void QueryRecordCmd( void )
3812 {
3813 OSStatus err;
3814 DNSServiceRef sdRef;
3815 QueryRecordContext * context = NULL;
3816 dispatch_source_t signalSource = NULL;
3817 int useMainConnection;
3818
3819 // Set up SIGINT handler.
3820
3821 signal( SIGINT, SIG_IGN );
3822 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3823 require_noerr( err, exit );
3824 dispatch_resume( signalSource );
3825
3826 // Create context.
3827
3828 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3829 require_action( context, exit, err = kNoMemoryErr );
3830
3831 // Check command parameters.
3832
3833 if( gQueryRecord_TimeLimitSecs < 0 )
3834 {
3835 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3836 err = kParamErr;
3837 goto exit;
3838 }
3839
3840 #if( MDNSRESPONDER_PROJECT )
3841 if( gFallbackDNSService )
3842 {
3843 err = _SetDefaultFallbackDNSService( gFallbackDNSService );
3844 require_noerr_quiet( err, exit );
3845 }
3846 #endif
3847 // Create main connection.
3848
3849 if( gConnectionOpt )
3850 {
3851 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3852 require_noerr_quiet( err, exit );
3853 useMainConnection = true;
3854 }
3855 else
3856 {
3857 useMainConnection = false;
3858 }
3859
3860 // Get flags.
3861
3862 context->flags = GetDNSSDFlagsFromOpts();
3863 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3864
3865 // Get interface.
3866
3867 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3868 require_noerr_quiet( err, exit );
3869
3870 // Get record type.
3871
3872 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3873 require_noerr( err, exit );
3874
3875 // Set remaining parameters.
3876
3877 context->recordName = gQueryRecord_Name;
3878 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3879 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3880 context->printRawRData = gQueryRecord_RawRData ? true : false;
3881
3882 // Print prologue.
3883
3884 QueryRecordPrintPrologue( context );
3885
3886 // Start operation.
3887
3888 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3889 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3890 kDNSServiceClass_IN, QueryRecordCallback, context );
3891 require_noerr( err, exit );
3892
3893 context->opRef = sdRef;
3894 if( !useMainConnection )
3895 {
3896 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3897 require_noerr( err, exit );
3898 }
3899
3900 // Set time limit.
3901
3902 if( context->timeLimitSecs > 0 )
3903 {
3904 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3905 Exit );
3906 }
3907 dispatch_main();
3908
3909 exit:
3910 dispatch_source_forget( &signalSource );
3911 if( context ) QueryRecordContextFree( context );
3912 if( err ) exit( 1 );
3913 }
3914
3915 //===========================================================================================================================
3916 // QueryRecordContextFree
3917 //===========================================================================================================================
3918
3919 static void QueryRecordContextFree( QueryRecordContext *inContext )
3920 {
3921 DNSServiceForget( &inContext->opRef );
3922 DNSServiceForget( &inContext->mainRef );
3923 free( inContext );
3924 }
3925
3926 //===========================================================================================================================
3927 // QueryRecordPrintPrologue
3928 //===========================================================================================================================
3929
3930 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3931 {
3932 const int timeLimitSecs = inContext->timeLimitSecs;
3933 char ifName[ kInterfaceNameBufLen ];
3934
3935 InterfaceIndexToName( inContext->ifIndex, ifName );
3936
3937 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3938 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3939 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3940 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3941 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3942 FPrintF( stdout, "Time limit: " );
3943 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3944 else FPrintF( stdout, "∞\n" );
3945 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3946 FPrintF( stdout, "---\n" );
3947
3948 }
3949
3950 //===========================================================================================================================
3951 // QueryRecordCallback
3952 //===========================================================================================================================
3953
3954 static void DNSSD_API
3955 QueryRecordCallback(
3956 DNSServiceRef inSDRef,
3957 DNSServiceFlags inFlags,
3958 uint32_t inInterfaceIndex,
3959 DNSServiceErrorType inError,
3960 const char * inFullName,
3961 uint16_t inType,
3962 uint16_t inClass,
3963 uint16_t inRDataLen,
3964 const void * inRDataPtr,
3965 uint32_t inTTL,
3966 void * inContext )
3967 {
3968 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3969 struct timeval now;
3970 OSStatus err;
3971 char * rdataStrMem = NULL;
3972 const char * rdataStr;
3973
3974 Unused( inSDRef );
3975
3976 gettimeofday( &now, NULL );
3977
3978 switch( inError )
3979 {
3980 case kDNSServiceErr_NoError:
3981 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, &rdataStrMem );
3982 if( !rdataStrMem )
3983 {
3984 ASPrintF( &rdataStrMem, "%#H", inRDataPtr, (int) inRDataLen, INT_MAX );
3985 require_action( rdataStrMem, exit, err = kNoMemoryErr );
3986 }
3987 rdataStr = rdataStrMem;
3988 break;
3989
3990 case kDNSServiceErr_NoSuchRecord:
3991 rdataStr = kNoSuchRecordStr;
3992 break;
3993
3994 case kDNSServiceErr_NoSuchName:
3995 rdataStr = kNoSuchNameStr;
3996 break;
3997
3998 case kDNSServiceErr_Timeout:
3999 Exit( kExitReason_Timeout );
4000
4001 default:
4002 err = inError;
4003 goto exit;
4004 }
4005 if( !context->printedHeader )
4006 {
4007 FPrintF( stdout, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
4008 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
4009 context->printedHeader = true;
4010 }
4011 FPrintF( stdout, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
4012 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
4013 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr );
4014
4015 if( context->oneShotMode )
4016 {
4017 if( ( inFlags & kDNSServiceFlagsAdd ) &&
4018 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
4019 {
4020 context->gotRecord = true;
4021 }
4022 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
4023 }
4024 err = kNoErr;
4025
4026 exit:
4027 ForgetMem( &rdataStrMem );
4028 if( err ) ErrQuit( 1, "error: %#m\n", err );
4029 }
4030
4031 //===========================================================================================================================
4032 // RegisterCmd
4033 //===========================================================================================================================
4034
4035 typedef struct
4036 {
4037 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
4038 uint8_t * dataPtr; // Record data.
4039 size_t dataLen; // Record data length.
4040 uint32_t ttl; // Record TTL value.
4041 uint16_t type; // Record type.
4042
4043 } ExtraRecord;
4044
4045 typedef struct
4046 {
4047 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
4048 const char * name; // Service name argument for DNSServiceRegister().
4049 const char * type; // Service type argument for DNSServiceRegister().
4050 const char * domain; // Domain in which advertise the service.
4051 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
4052 size_t txtLen; // Service TXT record data len.
4053 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
4054 size_t extraRecordsCount; // Number of extra records.
4055 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
4056 size_t updateTXTLen; // Length of record data for TXT record update.
4057 uint32_t updateTTL; // TTL of updated TXT record.
4058 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
4059 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
4060 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
4061 int lifetimeMs; // Lifetime of the record registration in milliseconds.
4062 uint16_t port; // Service instance's port number.
4063 Boolean printedHeader; // True if results header was printed.
4064 Boolean didRegister; // True if service was registered.
4065
4066 } RegisterContext;
4067
4068 static void RegisterPrintPrologue( const RegisterContext *inContext );
4069 static void RegisterContextFree( RegisterContext *inContext );
4070 static void DNSSD_API
4071 RegisterCallback(
4072 DNSServiceRef inSDRef,
4073 DNSServiceFlags inFlags,
4074 DNSServiceErrorType inError,
4075 const char * inName,
4076 const char * inType,
4077 const char * inDomain,
4078 void * inContext );
4079 static void RegisterUpdate( void *inContext );
4080
4081 static void RegisterCmd( void )
4082 {
4083 OSStatus err;
4084 RegisterContext * context = NULL;
4085 dispatch_source_t signalSource = NULL;
4086
4087 // Set up SIGINT handler.
4088
4089 signal( SIGINT, SIG_IGN );
4090 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4091 require_noerr( err, exit );
4092 dispatch_resume( signalSource );
4093
4094 // Create context.
4095
4096 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
4097 require_action( context, exit, err = kNoMemoryErr );
4098
4099 // Check command parameters.
4100
4101 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
4102 {
4103 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
4104 err = kParamErr;
4105 goto exit;
4106 }
4107
4108 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
4109 {
4110 FPrintF( stderr, "There are missing additional record parameters.\n" );
4111 err = kParamErr;
4112 goto exit;
4113 }
4114
4115 // Get flags.
4116
4117 context->flags = GetDNSSDFlagsFromOpts();
4118
4119 // Get interface index.
4120
4121 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4122 require_noerr_quiet( err, exit );
4123
4124 // Get TXT record data.
4125
4126 if( gRegister_TXT )
4127 {
4128 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
4129 require_noerr_quiet( err, exit );
4130 }
4131
4132 // Set remaining parameters.
4133
4134 context->name = gRegister_Name;
4135 context->type = gRegister_Type;
4136 context->domain = gRegister_Domain;
4137 context->port = (uint16_t) gRegister_Port;
4138 context->lifetimeMs = gRegister_LifetimeMs;
4139
4140 if( gAddRecord_TypesCount > 0 )
4141 {
4142 size_t i;
4143
4144 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
4145 require_action( context, exit, err = kNoMemoryErr );
4146 context->extraRecordsCount = gAddRecord_TypesCount;
4147
4148 for( i = 0; i < gAddRecord_TypesCount; ++i )
4149 {
4150 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
4151
4152 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
4153 require_noerr( err, exit );
4154
4155 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
4156 if( err )
4157 {
4158 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
4159 err = kParamErr;
4160 goto exit;
4161 }
4162
4163 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
4164 require_noerr_quiet( err, exit );
4165 }
4166 }
4167
4168 if( gUpdateRecord_Data )
4169 {
4170 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
4171 require_noerr_quiet( err, exit );
4172
4173 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
4174 context->updateDelayMs = gUpdateRecord_DelayMs;
4175 }
4176
4177 // Print prologue.
4178
4179 RegisterPrintPrologue( context );
4180
4181 // Start operation.
4182
4183 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
4184 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
4185 RegisterCallback, context );
4186 ForgetMem( &context->txtPtr );
4187 require_noerr( err, exit );
4188
4189 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4190 require_noerr( err, exit );
4191
4192 dispatch_main();
4193
4194 exit:
4195 dispatch_source_forget( &signalSource );
4196 if( context ) RegisterContextFree( context );
4197 if( err ) exit( 1 );
4198 }
4199
4200 //===========================================================================================================================
4201 // RegisterPrintPrologue
4202 //===========================================================================================================================
4203
4204 static void RegisterPrintPrologue( const RegisterContext *inContext )
4205 {
4206 size_t i;
4207 int infinite;
4208 char ifName[ kInterfaceNameBufLen ];
4209
4210 InterfaceIndexToName( inContext->ifIndex, ifName );
4211
4212 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4213 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4214 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
4215 FPrintF( stdout, "Type: %s\n", inContext->type );
4216 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
4217 FPrintF( stdout, "Port: %u\n", inContext->port );
4218 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
4219 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
4220 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
4221 if( inContext->updateTXTPtr )
4222 {
4223 FPrintF( stdout, "\nUpdate record:\n" );
4224 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
4225 FPrintF( stdout, " TTL: %u%?s\n",
4226 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4227 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
4228 }
4229 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
4230 for( i = 0; i < inContext->extraRecordsCount; ++i )
4231 {
4232 const ExtraRecord * record = &inContext->extraRecords[ i ];
4233
4234 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
4235 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
4236 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
4237 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
4238 }
4239 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4240 FPrintF( stdout, "---\n" );
4241 }
4242
4243 //===========================================================================================================================
4244 // RegisterContextFree
4245 //===========================================================================================================================
4246
4247 static void RegisterContextFree( RegisterContext *inContext )
4248 {
4249 ExtraRecord * record;
4250 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
4251
4252 DNSServiceForget( &inContext->opRef );
4253 ForgetMem( &inContext->txtPtr );
4254 for( record = inContext->extraRecords; record < end; ++record )
4255 {
4256 check( !record->recordRef );
4257 ForgetMem( &record->dataPtr );
4258 }
4259 ForgetMem( &inContext->extraRecords );
4260 ForgetMem( &inContext->updateTXTPtr );
4261 free( inContext );
4262 }
4263
4264 //===========================================================================================================================
4265 // RegisterCallback
4266 //===========================================================================================================================
4267
4268 static void DNSSD_API
4269 RegisterCallback(
4270 DNSServiceRef inSDRef,
4271 DNSServiceFlags inFlags,
4272 DNSServiceErrorType inError,
4273 const char * inName,
4274 const char * inType,
4275 const char * inDomain,
4276 void * inContext )
4277 {
4278 RegisterContext * const context = (RegisterContext *) inContext;
4279 OSStatus err;
4280 struct timeval now;
4281
4282 Unused( inSDRef );
4283
4284 gettimeofday( &now, NULL );
4285
4286 if( !context->printedHeader )
4287 {
4288 FPrintF( stdout, "%-26s %-16s Service\n", "Timestamp", "Flags" );
4289 context->printedHeader = true;
4290 }
4291 FPrintF( stdout, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
4292
4293 require_noerr_action_quiet( inError, exit, err = inError );
4294
4295 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
4296 {
4297 context->didRegister = true;
4298 if( context->updateTXTPtr )
4299 {
4300 if( context->updateDelayMs > 0 )
4301 {
4302 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4303 context, RegisterUpdate );
4304 }
4305 else
4306 {
4307 RegisterUpdate( context );
4308 }
4309 }
4310 if( context->extraRecordsCount > 0 )
4311 {
4312 ExtraRecord * record;
4313 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
4314
4315 for( record = context->extraRecords; record < end; ++record )
4316 {
4317 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
4318 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
4319 require_noerr( err, exit );
4320 }
4321 }
4322 if( context->lifetimeMs == 0 )
4323 {
4324 Exit( kExitReason_TimeLimit );
4325 }
4326 else if( context->lifetimeMs > 0 )
4327 {
4328 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4329 kExitReason_TimeLimit, Exit );
4330 }
4331 }
4332 err = kNoErr;
4333
4334 exit:
4335 if( err ) exit( 1 );
4336 }
4337
4338 //===========================================================================================================================
4339 // RegisterUpdate
4340 //===========================================================================================================================
4341
4342 static void RegisterUpdate( void *inContext )
4343 {
4344 OSStatus err;
4345 RegisterContext * const context = (RegisterContext *) inContext;
4346
4347 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
4348 context->updateTTL );
4349 require_noerr( err, exit );
4350
4351 exit:
4352 if( err ) exit( 1 );
4353 }
4354
4355 //===========================================================================================================================
4356 // RegisterRecordCmd
4357 //===========================================================================================================================
4358
4359 typedef struct
4360 {
4361 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
4362 DNSRecordRef recordRef; // Registered record reference.
4363 const char * recordName; // Name of resource record.
4364 uint8_t * dataPtr; // Pointer to resource record data.
4365 size_t dataLen; // Length of resource record data.
4366 uint32_t ttl; // TTL value of resource record in seconds.
4367 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
4368 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
4369 int lifetimeMs; // Lifetime of the record registration in milliseconds.
4370 uint16_t recordType; // Resource record type.
4371 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
4372 size_t updateDataLen; // Length of data for record update.
4373 uint32_t updateTTL; // TTL for updated record.
4374 int updateDelayMs; // Post-registration record update delay in milliseconds.
4375 Boolean didRegister; // True if the record was registered.
4376
4377 } RegisterRecordContext;
4378
4379 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
4380 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
4381 static void DNSSD_API
4382 RegisterRecordCallback(
4383 DNSServiceRef inSDRef,
4384 DNSRecordRef inRecordRef,
4385 DNSServiceFlags inFlags,
4386 DNSServiceErrorType inError,
4387 void * inContext );
4388 static void RegisterRecordUpdate( void *inContext );
4389
4390 static void RegisterRecordCmd( void )
4391 {
4392 OSStatus err;
4393 RegisterRecordContext * context = NULL;
4394 dispatch_source_t signalSource = NULL;
4395
4396 // Set up SIGINT handler.
4397
4398 signal( SIGINT, SIG_IGN );
4399 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4400 require_noerr( err, exit );
4401 dispatch_resume( signalSource );
4402
4403 // Create context.
4404
4405 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
4406 require_action( context, exit, err = kNoMemoryErr );
4407
4408 // Create connection.
4409
4410 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
4411 require_noerr_quiet( err, exit );
4412
4413 // Get flags.
4414
4415 context->flags = GetDNSSDFlagsFromOpts();
4416
4417 // Get interface.
4418
4419 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4420 require_noerr_quiet( err, exit );
4421
4422 // Get record type.
4423
4424 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
4425 require_noerr( err, exit );
4426
4427 // Get record data.
4428
4429 if( gRegisterRecord_Data )
4430 {
4431 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
4432 require_noerr_quiet( err, exit );
4433 }
4434
4435 // Set remaining parameters.
4436
4437 context->recordName = gRegisterRecord_Name;
4438 context->ttl = (uint32_t) gRegisterRecord_TTL;
4439 context->lifetimeMs = gRegisterRecord_LifetimeMs;
4440
4441 // Get update data.
4442
4443 if( gRegisterRecord_UpdateData )
4444 {
4445 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
4446 require_noerr_quiet( err, exit );
4447
4448 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
4449 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
4450 }
4451
4452 // Print prologue.
4453
4454 RegisterRecordPrintPrologue( context );
4455
4456 // Start operation.
4457
4458 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
4459 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
4460 context->ttl, RegisterRecordCallback, context );
4461 if( err )
4462 {
4463 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
4464 goto exit;
4465 }
4466
4467 dispatch_main();
4468
4469 exit:
4470 dispatch_source_forget( &signalSource );
4471 if( context ) RegisterRecordContextFree( context );
4472 if( err ) exit( 1 );
4473 }
4474
4475 //===========================================================================================================================
4476 // RegisterRecordPrintPrologue
4477 //===========================================================================================================================
4478
4479 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
4480 {
4481 int infinite;
4482 char ifName[ kInterfaceNameBufLen ];
4483
4484 InterfaceIndexToName( inContext->ifIndex, ifName );
4485
4486 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4487 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4488 FPrintF( stdout, "Name: %s\n", inContext->recordName );
4489 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
4490 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
4491 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
4492 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
4493 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
4494 if( inContext->updateDataPtr )
4495 {
4496 FPrintF( stdout, "\nUpdate record:\n" );
4497 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
4498 FPrintF( stdout, " TTL: %u%?s\n",
4499 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4500 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
4501 }
4502 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4503 FPrintF( stdout, "---\n" );
4504 }
4505
4506 //===========================================================================================================================
4507 // RegisterRecordContextFree
4508 //===========================================================================================================================
4509
4510 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
4511 {
4512 DNSServiceForget( &inContext->conRef );
4513 ForgetMem( &inContext->dataPtr );
4514 ForgetMem( &inContext->updateDataPtr );
4515 free( inContext );
4516 }
4517
4518 //===========================================================================================================================
4519 // RegisterRecordCallback
4520 //===========================================================================================================================
4521
4522 static void
4523 RegisterRecordCallback(
4524 DNSServiceRef inSDRef,
4525 DNSRecordRef inRecordRef,
4526 DNSServiceFlags inFlags,
4527 DNSServiceErrorType inError,
4528 void * inContext )
4529 {
4530 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4531 struct timeval now;
4532
4533 Unused( inSDRef );
4534 Unused( inRecordRef );
4535 Unused( inFlags );
4536 Unused( context );
4537
4538 gettimeofday( &now, NULL );
4539 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4540
4541 if( !context->didRegister && !inError )
4542 {
4543 context->didRegister = true;
4544 if( context->updateDataPtr )
4545 {
4546 if( context->updateDelayMs > 0 )
4547 {
4548 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4549 context, RegisterRecordUpdate );
4550 }
4551 else
4552 {
4553 RegisterRecordUpdate( context );
4554 }
4555 }
4556 if( context->lifetimeMs == 0 )
4557 {
4558 Exit( kExitReason_TimeLimit );
4559 }
4560 else if( context->lifetimeMs > 0 )
4561 {
4562 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4563 kExitReason_TimeLimit, Exit );
4564 }
4565 }
4566 }
4567
4568 //===========================================================================================================================
4569 // RegisterRecordUpdate
4570 //===========================================================================================================================
4571
4572 static void RegisterRecordUpdate( void *inContext )
4573 {
4574 OSStatus err;
4575 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4576
4577 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4578 context->updateDataPtr, context->updateTTL );
4579 require_noerr( err, exit );
4580
4581 exit:
4582 if( err ) exit( 1 );
4583 }
4584
4585 //===========================================================================================================================
4586 // ResolveCmd
4587 //===========================================================================================================================
4588
4589 typedef struct
4590 {
4591 DNSServiceRef mainRef; // Main sdRef for shared connections.
4592 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4593 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4594 const char * name; // Service name argument for DNSServiceResolve().
4595 const char * type; // Service type argument for DNSServiceResolve().
4596 const char * domain; // Domain argument for DNSServiceResolve().
4597 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4598 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4599
4600 } ResolveContext;
4601
4602 static void ResolvePrintPrologue( const ResolveContext *inContext );
4603 static void ResolveContextFree( ResolveContext *inContext );
4604 static void DNSSD_API
4605 ResolveCallback(
4606 DNSServiceRef inSDRef,
4607 DNSServiceFlags inFlags,
4608 uint32_t inInterfaceIndex,
4609 DNSServiceErrorType inError,
4610 const char * inFullName,
4611 const char * inHostname,
4612 uint16_t inPort,
4613 uint16_t inTXTLen,
4614 const unsigned char * inTXTPtr,
4615 void * inContext );
4616
4617 static void ResolveCmd( void )
4618 {
4619 OSStatus err;
4620 DNSServiceRef sdRef;
4621 ResolveContext * context = NULL;
4622 dispatch_source_t signalSource = NULL;
4623 int useMainConnection;
4624
4625 // Set up SIGINT handler.
4626
4627 signal( SIGINT, SIG_IGN );
4628 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4629 require_noerr( err, exit );
4630 dispatch_resume( signalSource );
4631
4632 // Create context.
4633
4634 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4635 require_action( context, exit, err = kNoMemoryErr );
4636
4637 // Check command parameters.
4638
4639 if( gResolve_TimeLimitSecs < 0 )
4640 {
4641 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4642 err = kParamErr;
4643 goto exit;
4644 }
4645
4646 // Create main connection.
4647
4648 if( gConnectionOpt )
4649 {
4650 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4651 require_noerr_quiet( err, exit );
4652 useMainConnection = true;
4653 }
4654 else
4655 {
4656 useMainConnection = false;
4657 }
4658
4659 // Get flags.
4660
4661 context->flags = GetDNSSDFlagsFromOpts();
4662 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4663
4664 // Get interface index.
4665
4666 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4667 require_noerr_quiet( err, exit );
4668
4669 // Set remaining parameters.
4670
4671 context->name = gResolve_Name;
4672 context->type = gResolve_Type;
4673 context->domain = gResolve_Domain;
4674 context->timeLimitSecs = gResolve_TimeLimitSecs;
4675
4676 // Print prologue.
4677
4678 ResolvePrintPrologue( context );
4679
4680 // Start operation.
4681
4682 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4683 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4684 ResolveCallback, NULL );
4685 require_noerr( err, exit );
4686
4687 context->opRef = sdRef;
4688 if( !useMainConnection )
4689 {
4690 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4691 require_noerr( err, exit );
4692 }
4693
4694 // Set time limit.
4695
4696 if( context->timeLimitSecs > 0 )
4697 {
4698 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4699 kExitReason_TimeLimit, Exit );
4700 }
4701 dispatch_main();
4702
4703 exit:
4704 dispatch_source_forget( &signalSource );
4705 if( context ) ResolveContextFree( context );
4706 if( err ) exit( 1 );
4707 }
4708
4709 //===========================================================================================================================
4710 // ReconfirmCmd
4711 //===========================================================================================================================
4712
4713 static void ReconfirmCmd( void )
4714 {
4715 OSStatus err;
4716 uint8_t * rdataPtr = NULL;
4717 size_t rdataLen = 0;
4718 DNSServiceFlags flags;
4719 uint32_t ifIndex;
4720 uint16_t type, class;
4721 char ifName[ kInterfaceNameBufLen ];
4722
4723 // Get flags.
4724
4725 flags = GetDNSSDFlagsFromOpts();
4726
4727 // Get interface index.
4728
4729 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4730 require_noerr_quiet( err, exit );
4731
4732 // Get record type.
4733
4734 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4735 require_noerr( err, exit );
4736
4737 // Get record data.
4738
4739 if( gReconfirmRecord_Data )
4740 {
4741 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4742 require_noerr_quiet( err, exit );
4743 }
4744
4745 // Get record class.
4746
4747 if( gReconfirmRecord_Class )
4748 {
4749 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4750 require_noerr( err, exit );
4751 }
4752 else
4753 {
4754 class = kDNSServiceClass_IN;
4755 }
4756
4757 // Print prologue.
4758
4759 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4760 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4761 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4762 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4763 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4764 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4765 FPrintF( stdout, "---\n" );
4766
4767 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4768 FPrintF( stdout, "Error: %#m\n", err );
4769
4770 exit:
4771 FreeNullSafe( rdataPtr );
4772 if( err ) exit( 1 );
4773 }
4774
4775 //===========================================================================================================================
4776 // ResolvePrintPrologue
4777 //===========================================================================================================================
4778
4779 static void ResolvePrintPrologue( const ResolveContext *inContext )
4780 {
4781 const int timeLimitSecs = inContext->timeLimitSecs;
4782 char ifName[ kInterfaceNameBufLen ];
4783
4784 InterfaceIndexToName( inContext->ifIndex, ifName );
4785
4786 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4787 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4788 FPrintF( stdout, "Name: %s\n", inContext->name );
4789 FPrintF( stdout, "Type: %s\n", inContext->type );
4790 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4791 FPrintF( stdout, "Time limit: " );
4792 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4793 else FPrintF( stdout, "∞\n" );
4794 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4795 FPrintF( stdout, "---\n" );
4796 }
4797
4798 //===========================================================================================================================
4799 // ResolveContextFree
4800 //===========================================================================================================================
4801
4802 static void ResolveContextFree( ResolveContext *inContext )
4803 {
4804 DNSServiceForget( &inContext->opRef );
4805 DNSServiceForget( &inContext->mainRef );
4806 free( inContext );
4807 }
4808
4809 //===========================================================================================================================
4810 // ResolveCallback
4811 //===========================================================================================================================
4812
4813 static void DNSSD_API
4814 ResolveCallback(
4815 DNSServiceRef inSDRef,
4816 DNSServiceFlags inFlags,
4817 uint32_t inInterfaceIndex,
4818 DNSServiceErrorType inError,
4819 const char * inFullName,
4820 const char * inHostname,
4821 uint16_t inPort,
4822 uint16_t inTXTLen,
4823 const unsigned char * inTXTPtr,
4824 void * inContext )
4825 {
4826 struct timeval now;
4827 char errorStr[ 64 ];
4828
4829 Unused( inSDRef );
4830 Unused( inFlags );
4831 Unused( inContext );
4832
4833 gettimeofday( &now, NULL );
4834
4835 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4836
4837 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4838 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4839 if( inTXTLen == 1 )
4840 {
4841 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4842 }
4843 else
4844 {
4845 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4846 }
4847 }
4848
4849 //===========================================================================================================================
4850 // GetAddrInfoPOSIXCmd
4851 //===========================================================================================================================
4852
4853 #define AddressFamilyStr( X ) ( \
4854 ( (X) == AF_INET ) ? "inet" : \
4855 ( (X) == AF_INET6 ) ? "inet6" : \
4856 ( (X) == AF_UNSPEC ) ? "unspec" : \
4857 "???" )
4858
4859 typedef struct
4860 {
4861 unsigned int flag;
4862 const char * str;
4863
4864 } FlagStringPair;
4865
4866 #define CaseFlagStringify( X ) { (X), # X }
4867
4868 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4869 {
4870 #if( defined( AI_UNUSABLE ) )
4871 CaseFlagStringify( AI_UNUSABLE ),
4872 #endif
4873 CaseFlagStringify( AI_NUMERICSERV ),
4874 CaseFlagStringify( AI_V4MAPPED ),
4875 CaseFlagStringify( AI_ADDRCONFIG ),
4876 #if( defined( AI_V4MAPPED_CFG ) )
4877 CaseFlagStringify( AI_V4MAPPED_CFG ),
4878 #endif
4879 CaseFlagStringify( AI_ALL ),
4880 CaseFlagStringify( AI_NUMERICHOST ),
4881 CaseFlagStringify( AI_CANONNAME ),
4882 CaseFlagStringify( AI_PASSIVE ),
4883 { 0, NULL }
4884 };
4885
4886 static void GetAddrInfoPOSIXCmd( void )
4887 {
4888 OSStatus err;
4889 struct addrinfo hints;
4890 struct timeval now;
4891 const struct addrinfo * addrInfo;
4892 struct addrinfo * addrInfoList = NULL;
4893 const FlagStringPair * pair;
4894
4895 memset( &hints, 0, sizeof( hints ) );
4896 hints.ai_socktype = SOCK_STREAM;
4897
4898 // Set hints address family.
4899
4900 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4901 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4902 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4903 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4904 else
4905 {
4906 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4907 err = kParamErr;
4908 goto exit;
4909 }
4910
4911 // Set hints flags.
4912
4913 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4914 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4915 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4916 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4917 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4918 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4919 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4920 #if( defined( AI_V4MAPPED_CFG ) )
4921 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4922 #endif
4923 #if( defined( AI_DEFAULT ) )
4924 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4925 #endif
4926 #if( defined( AI_UNUSABLE ) )
4927 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4928 #endif
4929
4930 #if( MDNSRESPONDER_PROJECT )
4931 if( gFallbackDNSService )
4932 {
4933 err = _SetDefaultFallbackDNSService( gFallbackDNSService );
4934 require_noerr_quiet( err, exit );
4935 }
4936 #endif
4937 // Print prologue.
4938
4939 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4940 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4941 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4942 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4943 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4944 {
4945 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4946 }
4947 FPrintF( stdout, ">\n" );
4948 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4949 FPrintF( stdout, "---\n" );
4950
4951 // Call getaddrinfo().
4952
4953 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4954 gettimeofday( &now, NULL );
4955 if( err )
4956 {
4957 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4958 }
4959 else
4960 {
4961 int addrCount = 0;
4962
4963 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4964
4965 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4966 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4967 {
4968 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4969 }
4970 }
4971 FPrintF( stdout, "---\n" );
4972 FPrintF( stdout, "End time: %{du:time}\n", &now );
4973
4974 exit:
4975 if( addrInfoList ) freeaddrinfo( addrInfoList );
4976 if( err ) exit( 1 );
4977 }
4978
4979 //===========================================================================================================================
4980 // ReverseLookupCmd
4981 //===========================================================================================================================
4982
4983 static void ReverseLookupCmd( void )
4984 {
4985 OSStatus err;
4986 QueryRecordContext * context = NULL;
4987 DNSServiceRef sdRef;
4988 dispatch_source_t signalSource = NULL;
4989 uint32_t ipv4Addr;
4990 uint8_t ipv6Addr[ 16 ];
4991 char recordName[ kReverseIPv6DomainNameBufLen ];
4992 int useMainConnection;
4993 const char * endPtr;
4994
4995 // Set up SIGINT handler.
4996
4997 signal( SIGINT, SIG_IGN );
4998 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4999 require_noerr( err, exit );
5000 dispatch_resume( signalSource );
5001
5002 // Create context.
5003
5004 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
5005 require_action( context, exit, err = kNoMemoryErr );
5006
5007 // Check command parameters.
5008
5009 if( gReverseLookup_TimeLimitSecs < 0 )
5010 {
5011 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
5012 err = kParamErr;
5013 goto exit;
5014 }
5015
5016 // Create main connection.
5017
5018 if( gConnectionOpt )
5019 {
5020 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
5021 require_noerr_quiet( err, exit );
5022 useMainConnection = true;
5023 }
5024 else
5025 {
5026 useMainConnection = false;
5027 }
5028
5029 // Get flags.
5030
5031 context->flags = GetDNSSDFlagsFromOpts();
5032 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
5033
5034 // Get interface index.
5035
5036 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5037 require_noerr_quiet( err, exit );
5038
5039 // Create reverse lookup record name.
5040
5041 err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
5042 &ipv4Addr, NULL, NULL, NULL, &endPtr );
5043 if( err || ( *endPtr != '\0' ) )
5044 {
5045 err = _StringToIPv6Address( gReverseLookup_IPAddr,
5046 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
5047 ipv6Addr, NULL, NULL, NULL, &endPtr );
5048 if( err || ( *endPtr != '\0' ) )
5049 {
5050 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
5051 err = kParamErr;
5052 goto exit;
5053 }
5054 _WriteReverseIPv6DomainNameString( ipv6Addr, recordName );
5055 }
5056 else
5057 {
5058 _WriteReverseIPv4DomainNameString( ipv4Addr, recordName );
5059 }
5060
5061 // Set remaining parameters.
5062
5063 context->recordName = recordName;
5064 context->recordType = kDNSServiceType_PTR;
5065 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
5066 context->oneShotMode = gReverseLookup_OneShot ? true : false;
5067
5068 // Print prologue.
5069
5070 QueryRecordPrintPrologue( context );
5071
5072 // Start operation.
5073
5074 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
5075 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
5076 kDNSServiceClass_IN, QueryRecordCallback, context );
5077 require_noerr( err, exit );
5078
5079 context->opRef = sdRef;
5080 if( !useMainConnection )
5081 {
5082 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
5083 require_noerr( err, exit );
5084 }
5085
5086 // Set time limit.
5087
5088 if( context->timeLimitSecs > 0 )
5089 {
5090 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
5091 kExitReason_TimeLimit, Exit );
5092 }
5093 dispatch_main();
5094
5095 exit:
5096 dispatch_source_forget( &signalSource );
5097 if( context ) QueryRecordContextFree( context );
5098 if( err ) exit( 1 );
5099 }
5100
5101 //===========================================================================================================================
5102 // PortMappingCmd
5103 //===========================================================================================================================
5104
5105 typedef struct
5106 {
5107 DNSServiceRef mainRef; // Main sdRef for shared connection.
5108 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
5109 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
5110 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
5111 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
5112 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
5113 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
5114 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
5115 Boolean printedHeader; // True if results header was printed.
5116
5117 } PortMappingContext;
5118
5119 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
5120 static void PortMappingContextFree( PortMappingContext *inContext );
5121 static void DNSSD_API
5122 PortMappingCallback(
5123 DNSServiceRef inSDRef,
5124 DNSServiceFlags inFlags,
5125 uint32_t inInterfaceIndex,
5126 DNSServiceErrorType inError,
5127 uint32_t inExternalIPv4Address,
5128 DNSServiceProtocol inProtocol,
5129 uint16_t inInternalPort,
5130 uint16_t inExternalPort,
5131 uint32_t inTTL,
5132 void * inContext );
5133
5134 static void PortMappingCmd( void )
5135 {
5136 OSStatus err;
5137 PortMappingContext * context = NULL;
5138 DNSServiceRef sdRef;
5139 dispatch_source_t signalSource = NULL;
5140 int useMainConnection;
5141
5142 // Set up SIGINT handler.
5143
5144 signal( SIGINT, SIG_IGN );
5145 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
5146 require_noerr( err, exit );
5147 dispatch_resume( signalSource );
5148
5149 // Create context.
5150
5151 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
5152 require_action( context, exit, err = kNoMemoryErr );
5153
5154 // Check command parameters.
5155
5156 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
5157 {
5158 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
5159 err = kParamErr;
5160 goto exit;
5161 }
5162
5163 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
5164 {
5165 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
5166 err = kParamErr;
5167 goto exit;
5168 }
5169
5170 // Create main connection.
5171
5172 if( gConnectionOpt )
5173 {
5174 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
5175 require_noerr_quiet( err, exit );
5176 useMainConnection = true;
5177 }
5178 else
5179 {
5180 useMainConnection = false;
5181 }
5182
5183 // Get flags.
5184
5185 context->flags = GetDNSSDFlagsFromOpts();
5186 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
5187
5188 // Get interface index.
5189
5190 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5191 require_noerr_quiet( err, exit );
5192
5193 // Set remaining parameters.
5194
5195 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
5196 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
5197 context->ttl = (uint32_t) gPortMapping_TTL;
5198 context->internalPort = (uint16_t) gPortMapping_InternalPort;
5199 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
5200
5201 // Print prologue.
5202
5203 PortMappingPrintPrologue( context );
5204
5205 // Start operation.
5206
5207 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
5208 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
5209 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
5210 require_noerr( err, exit );
5211
5212 context->opRef = sdRef;
5213 if( !useMainConnection )
5214 {
5215 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
5216 require_noerr( err, exit );
5217 }
5218
5219 dispatch_main();
5220
5221 exit:
5222 dispatch_source_forget( &signalSource );
5223 if( context ) PortMappingContextFree( context );
5224 if( err ) exit( 1 );
5225 }
5226
5227 //===========================================================================================================================
5228 // PortMappingPrintPrologue
5229 //===========================================================================================================================
5230
5231 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
5232 {
5233 char ifName[ kInterfaceNameBufLen ];
5234
5235 InterfaceIndexToName( inContext->ifIndex, ifName );
5236
5237 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
5238 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
5239 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
5240 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
5241 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
5242 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
5243 " (system will use a default value.)" );
5244 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5245 FPrintF( stdout, "---\n" );
5246
5247 }
5248
5249 //===========================================================================================================================
5250 // PortMappingContextFree
5251 //===========================================================================================================================
5252
5253 static void PortMappingContextFree( PortMappingContext *inContext )
5254 {
5255 DNSServiceForget( &inContext->opRef );
5256 DNSServiceForget( &inContext->mainRef );
5257 free( inContext );
5258 }
5259
5260 //===========================================================================================================================
5261 // PortMappingCallback
5262 //===========================================================================================================================
5263
5264 static void DNSSD_API
5265 PortMappingCallback(
5266 DNSServiceRef inSDRef,
5267 DNSServiceFlags inFlags,
5268 uint32_t inInterfaceIndex,
5269 DNSServiceErrorType inError,
5270 uint32_t inExternalIPv4Address,
5271 DNSServiceProtocol inProtocol,
5272 uint16_t inInternalPort,
5273 uint16_t inExternalPort,
5274 uint32_t inTTL,
5275 void * inContext )
5276 {
5277 PortMappingContext * const context = (PortMappingContext *) inContext;
5278 struct timeval now;
5279 char errorStr[ 128 ];
5280
5281 Unused( inSDRef );
5282 Unused( inFlags );
5283
5284 gettimeofday( &now, NULL );
5285
5286 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
5287 if( !context->printedHeader )
5288 {
5289 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
5290 context->printedHeader = true;
5291 }
5292 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
5293 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
5294 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
5295 }
5296
5297 #if( TARGET_OS_DARWIN )
5298 //===========================================================================================================================
5299 // RegisterKACmd
5300 //===========================================================================================================================
5301
5302 typedef struct
5303 {
5304 dispatch_queue_t queue; // Serial queue for command's events.
5305 dispatch_semaphore_t doneSem; // Semaphore to signal when underlying command operation is done.
5306 sockaddr_ip local; // Connection's local IP address and port.
5307 sockaddr_ip remote; // Connection's remote IP address and port.
5308 DNSServiceFlags flags; // Flags to pass to DNSServiceSleepKeepalive_sockaddr().
5309 unsigned int timeout; // Timeout to pass to DNSServiceSleepKeepalive_sockaddr().
5310 DNSServiceRef keepalive; // DNSServiceSleepKeepalive_sockaddr operation.
5311 dispatch_source_t sourceSigInt; // Dispatch source for SIGINT.
5312 dispatch_source_t sourceSigTerm; // Dispatch source for SIGTERM.
5313 OSStatus error; // Command's error.
5314
5315 } RegisterKACmdContext;
5316
5317 static void _RegisterKACmdFree( RegisterKACmdContext *inCmd );
5318 static void _RegisterKACmdStart( void *inContext );
5319 static OSStatus _RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA );
5320
5321 static void RegisterKACmd( void )
5322 {
5323 OSStatus err;
5324 RegisterKACmdContext * cmd = NULL;
5325
5326 cmd = (RegisterKACmdContext *) calloc( 1, sizeof( *cmd ) );
5327 require_action( cmd, exit, err = kNoMemoryErr );
5328
5329 err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_LocalAddress, "local IP address", &cmd->local );
5330 require_noerr_quiet( err, exit );
5331
5332 err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_RemoteAddress, "remote IP address", &cmd->remote );
5333 require_noerr_quiet( err, exit );
5334
5335 err = CheckIntegerArgument( gRegisterKA_Timeout, "timeout", 0, INT_MAX );
5336 require_noerr_quiet( err, exit );
5337
5338 cmd->flags = GetDNSSDFlagsFromOpts();
5339 cmd->timeout = (unsigned int) gRegisterKA_Timeout;
5340
5341 // Start command.
5342
5343 cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.registerka-command", DISPATCH_QUEUE_SERIAL );
5344 require_action( cmd->queue, exit, err = kNoResourcesErr );
5345
5346 cmd->doneSem = dispatch_semaphore_create( 0 );
5347 require_action( cmd->doneSem, exit, err = kNoResourcesErr );
5348
5349 dispatch_async_f( cmd->queue, cmd, _RegisterKACmdStart );
5350 dispatch_semaphore_wait( cmd->doneSem, DISPATCH_TIME_FOREVER );
5351 if( cmd->error ) err = cmd->error;
5352
5353 FPrintF( stdout, "---\n" );
5354 FPrintF( stdout, "End time: %{du:time}\n", NULL );
5355
5356 exit:
5357 if( cmd ) _RegisterKACmdFree( cmd );
5358 gExitCode = err ? 1 : 0;
5359 }
5360
5361 //===========================================================================================================================
5362
5363 static void _RegisterKACmdFree( RegisterKACmdContext *inCmd )
5364 {
5365 check( !inCmd->keepalive );
5366 check( !inCmd->sourceSigInt );
5367 check( !inCmd->sourceSigTerm );
5368 dispatch_forget( &inCmd->queue );
5369 dispatch_forget( &inCmd->doneSem );
5370 free( inCmd );
5371 }
5372
5373 //===========================================================================================================================
5374
5375 static void _RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError );
5376 static void _RegisterKACmdSignalHandler( void *inContext );
5377 static void DNSSD_API _RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx );
5378
5379 static void _RegisterKACmdStart( void *inContext )
5380 {
5381 OSStatus err;
5382 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inContext;
5383
5384 signal( SIGINT, SIG_IGN );
5385 err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigInt );
5386 require_noerr( err, exit );
5387 dispatch_resume( cmd->sourceSigInt );
5388
5389 signal( SIGTERM, SIG_IGN );
5390 err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigTerm );
5391 require_noerr( err, exit );
5392 dispatch_resume( cmd->sourceSigTerm );
5393
5394 FPrintF( stdout, "Flags: %#{flags}\n", cmd->flags, kDNSServiceFlagsDescriptors );
5395 FPrintF( stdout, "Local: %##a\n", &cmd->local.sa );
5396 FPrintF( stdout, "Remote: %##a\n", &cmd->remote.sa );
5397 FPrintF( stdout, "Timeout: %u\n", cmd->timeout );
5398 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5399 FPrintF( stdout, "---\n" );
5400
5401 if( __builtin_available( macOS 10.15.4, iOS 13.2.2, watchOS 6.2, tvOS 13.2, * ) )
5402 {
5403 err = DNSServiceSleepKeepalive_sockaddr( &cmd->keepalive, cmd->flags, &cmd->local.sa, &cmd->remote.sa, cmd->timeout,
5404 _RegisterKACmdKeepaliveCallback, cmd );
5405 require_noerr( err, exit );
5406 }
5407 else
5408 {
5409 FPrintF( stderr, "error: DNSServiceSleepKeepalive_sockaddr() is not available on this OS.\n" );
5410 err = kUnsupportedErr;
5411 goto exit;
5412 }
5413 err = DNSServiceSetDispatchQueue( cmd->keepalive, cmd->queue );
5414 require_noerr( err, exit );
5415
5416 exit:
5417 if( err ) _RegisterKACmdStop( cmd, err );
5418 }
5419
5420 //===========================================================================================================================
5421
5422 static OSStatus _RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA )
5423 {
5424 OSStatus err;
5425 sockaddr_ip sip;
5426
5427 err = StringToSockAddr( inArgStr, &sip, sizeof( sip ), NULL );
5428 if( !err && ( ( sip.sa.sa_family == AF_INET ) || ( sip.sa.sa_family == AF_INET6 ) ) )
5429 {
5430 if( outSA ) SockAddrCopy( &sip, outSA );
5431 }
5432 else
5433 {
5434 FPrintF( stderr, "error: Invalid %s: '%s'\n", inArgName, inArgStr );
5435 err = kParamErr;
5436 }
5437 return( err );
5438 }
5439
5440 //===========================================================================================================================
5441
5442 static void _RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError )
5443 {
5444 if( !inCmd->error ) inCmd->error = inError;
5445 DNSServiceForget( &inCmd->keepalive );
5446 dispatch_source_forget( &inCmd->sourceSigInt );
5447 dispatch_source_forget( &inCmd->sourceSigTerm );
5448 dispatch_semaphore_signal( inCmd->doneSem );
5449 }
5450
5451 //===========================================================================================================================
5452
5453 static void _RegisterKACmdSignalHandler( void *inContext )
5454 {
5455 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inContext;
5456
5457 _RegisterKACmdStop( cmd, kNoErr );
5458 }
5459
5460 //===========================================================================================================================
5461
5462 static void DNSSD_API _RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
5463 {
5464 RegisterKACmdContext * const cmd = (RegisterKACmdContext *) inCtx;
5465
5466 Unused( inSDRef );
5467
5468 FPrintF( stdout, "%{du:time} Record registration result: %#m\n", NULL, inError );
5469 if( !cmd->error ) cmd->error = inError;
5470 }
5471 #endif // TARGET_OS_DARWIN
5472
5473 //===========================================================================================================================
5474 // BrowseAllCmd
5475 //===========================================================================================================================
5476
5477 typedef struct BrowseAllConnection BrowseAllConnection;
5478
5479 typedef struct
5480 {
5481 ServiceBrowserRef browser; // Service browser.
5482 ServiceBrowserResults * results; // Results from the service browser.
5483 BrowseAllConnection * connectionList; // List of connections.
5484 dispatch_source_t connectionTimer; // Timer for connection timeout.
5485 int connectionPendingCount; // Number of pending connections.
5486 int connectionTimeoutSecs; // Timeout value for connections in seconds.
5487
5488 } BrowseAllContext;
5489
5490 struct BrowseAllConnection
5491 {
5492 BrowseAllConnection * next; // Next connection object in list.
5493 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
5494 uint16_t port; // TCP port to connect to.
5495 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
5496 OSStatus status; // Status of connection. NoErr means connection succeeded.
5497 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
5498 int32_t refCount; // This object's reference count.
5499 BrowseAllContext * context; // Back pointer to parent context.
5500 };
5501
5502 static void _BrowseAllContextFree( BrowseAllContext *inContext );
5503 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
5504 static OSStatus
5505 _BrowseAllConnectionCreate(
5506 const struct sockaddr * inSockAddr,
5507 uint16_t inPort,
5508 BrowseAllContext * inContext,
5509 BrowseAllConnection ** outConnection );
5510 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
5511 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
5512 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
5513 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
5514 static void _BrowseAllExit( void *inContext );
5515
5516 static Boolean _IsServiceTypeTCP( const char *inServiceType );
5517
5518 static void BrowseAllCmd( void )
5519 {
5520 OSStatus err;
5521 BrowseAllContext * context = NULL;
5522 size_t i;
5523 uint32_t ifIndex;
5524 char ifName[ kInterfaceNameBufLen ];
5525
5526 // Check parameters.
5527
5528 if( gBrowseAll_BrowseTimeSecs <= 0 )
5529 {
5530 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
5531 err = kParamErr;
5532 goto exit;
5533 }
5534
5535 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
5536 require_action( context, exit, err = kNoMemoryErr );
5537
5538 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
5539 #if( TARGET_OS_POSIX )
5540 // Increase the open file descriptor limit for connection sockets.
5541
5542 if( context->connectionTimeoutSecs > 0 )
5543 {
5544 struct rlimit fdLimits;
5545
5546 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
5547 err = map_global_noerr_errno( err );
5548 require_noerr( err, exit );
5549
5550 if( fdLimits.rlim_cur < 4096 )
5551 {
5552 fdLimits.rlim_cur = 4096;
5553 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
5554 err = map_global_noerr_errno( err );
5555 require_noerr( err, exit );
5556 }
5557 }
5558 #endif
5559
5560 // Get interface index.
5561
5562 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5563 require_noerr_quiet( err, exit );
5564
5565 // Print prologue.
5566
5567 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5568 FPrintF( stdout, "Service types: ");
5569 if( gBrowseAll_ServiceTypesCount > 0 )
5570 {
5571 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
5572 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
5573 {
5574 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
5575 }
5576 FPrintF( stdout, "\n" );
5577 }
5578 else
5579 {
5580 FPrintF( stdout, "all services\n" );
5581 }
5582 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
5583 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
5584 FPrintF( stdout, "Connect timeout: %d second%?c\n",
5585 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
5586 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
5587 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5588 FPrintF( stdout, "---\n" );
5589
5590 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
5591 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
5592 require_noerr( err, exit );
5593
5594 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
5595 {
5596 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
5597 require_noerr( err, exit );
5598 }
5599 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
5600 ServiceBrowserStart( context->browser );
5601 dispatch_main();
5602
5603 exit:
5604 if( context ) _BrowseAllContextFree( context );
5605 if( err ) exit( 1 );
5606 }
5607
5608 //===========================================================================================================================
5609 // _BrowseAllContextFree
5610 //===========================================================================================================================
5611
5612 static void _BrowseAllContextFree( BrowseAllContext *inContext )
5613 {
5614 check( !inContext->browser );
5615 check( !inContext->connectionTimer );
5616 check( !inContext->connectionList );
5617 ForgetServiceBrowserResults( &inContext->results );
5618 free( inContext );
5619 }
5620
5621 //===========================================================================================================================
5622 // _BrowseAllServiceBrowserCallback
5623 //===========================================================================================================================
5624
5625 #define kDiscardProtocolPort 9
5626
5627 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
5628 {
5629 OSStatus err;
5630 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5631 SBRDomain * domain;
5632 SBRServiceType * type;
5633 SBRServiceInstance * instance;
5634 SBRIPAddress * ipaddr;
5635
5636 Unused( inError );
5637
5638 require_action( inResults, exit, err = kUnexpectedErr );
5639
5640 check( !context->results );
5641 context->results = inResults;
5642 ServiceBrowserResultsRetain( context->results );
5643
5644 check( context->connectionPendingCount == 0 );
5645 if( context->connectionTimeoutSecs > 0 )
5646 {
5647 BrowseAllConnection * connection;
5648 BrowseAllConnection ** connectionPtr = &context->connectionList;
5649 char destination[ kSockAddrStringMaxSize ];
5650
5651 for( domain = context->results->domainList; domain; domain = domain->next )
5652 {
5653 for( type = domain->typeList; type; type = type->next )
5654 {
5655 if( !_IsServiceTypeTCP( type->name ) ) continue;
5656 for( instance = type->instanceList; instance; instance = instance->next )
5657 {
5658 if( instance->port == kDiscardProtocolPort ) continue;
5659 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5660 {
5661 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
5662 require_noerr( err, exit );
5663
5664 *connectionPtr = connection;
5665 connectionPtr = &connection->next;
5666
5667 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
5668 check_noerr( err );
5669 if( !err )
5670 {
5671 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
5672 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
5673 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5674 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
5675 dispatch_get_main_queue() );
5676 check_noerr( err );
5677 }
5678 if( !err )
5679 {
5680 _BrowseAllConnectionRetain( connection );
5681 connection->status = kInProgressErr;
5682 ++context->connectionPendingCount;
5683 }
5684 else
5685 {
5686 connection->status = err;
5687 }
5688 }
5689 }
5690 }
5691 }
5692 }
5693
5694 if( context->connectionPendingCount > 0 )
5695 {
5696 check( !context->connectionTimer );
5697 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5698 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5699 require_noerr( err, exit );
5700 dispatch_resume( context->connectionTimer );
5701 }
5702 else
5703 {
5704 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5705 }
5706 err = kNoErr;
5707
5708 exit:
5709 ForgetCF( &context->browser );
5710 if( err ) exit( 1 );
5711 }
5712
5713 //===========================================================================================================================
5714 // _BrowseAllConnectionCreate
5715 //===========================================================================================================================
5716
5717 static OSStatus
5718 _BrowseAllConnectionCreate(
5719 const struct sockaddr * inSockAddr,
5720 uint16_t inPort,
5721 BrowseAllContext * inContext,
5722 BrowseAllConnection ** outConnection )
5723 {
5724 OSStatus err;
5725 BrowseAllConnection * obj;
5726
5727 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5728 require_action( obj, exit, err = kNoMemoryErr );
5729
5730 obj->refCount = 1;
5731 SockAddrCopy( inSockAddr, &obj->sip );
5732 obj->port = inPort;
5733 obj->context = inContext;
5734
5735 *outConnection = obj;
5736 err = kNoErr;
5737
5738 exit:
5739 return( err );
5740 }
5741
5742 //===========================================================================================================================
5743 // _BrowseAllConnectionRetain
5744 //===========================================================================================================================
5745
5746 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5747 {
5748 ++inConnection->refCount;
5749 }
5750
5751 //===========================================================================================================================
5752 // _BrowseAllConnectionRelease
5753 //===========================================================================================================================
5754
5755 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5756 {
5757 if( --inConnection->refCount == 0 ) free( inConnection );
5758 }
5759
5760 //===========================================================================================================================
5761 // _BrowseAllConnectionProgress
5762 //===========================================================================================================================
5763
5764 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5765 {
5766 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5767
5768 if( inPhase == kAsyncConnectionPhase_Connected )
5769 {
5770 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5771
5772 connection->connectTimeSecs = info->connectSecs;
5773 }
5774 }
5775
5776 //===========================================================================================================================
5777 // _BrowseAllConnectionHandler
5778 //===========================================================================================================================
5779
5780 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5781 {
5782 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5783 BrowseAllContext * const context = connection->context;
5784
5785 connection->status = inError;
5786 ForgetSocket( &inSock );
5787 if( context )
5788 {
5789 check( context->connectionPendingCount > 0 );
5790 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5791 {
5792 dispatch_source_forget( &context->connectionTimer );
5793 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5794 }
5795 }
5796 _BrowseAllConnectionRelease( connection );
5797 }
5798
5799 //===========================================================================================================================
5800 // _BrowseAllExit
5801 //===========================================================================================================================
5802
5803 #define Indent( X ) ( (X) * 4 ), ""
5804
5805 static void _BrowseAllExit( void *inContext )
5806 {
5807 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5808 SBRDomain * domain;
5809 SBRServiceType * type;
5810 SBRServiceInstance * instance;
5811 SBRIPAddress * ipaddr;
5812 char textBuf[ 512 ];
5813 #if( TARGET_OS_POSIX )
5814 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5815 #endif
5816
5817 dispatch_source_forget( &context->connectionTimer );
5818
5819 for( domain = context->results->domainList; domain; domain = domain->next )
5820 {
5821 FPrintF( stdout, "%s\n\n", domain->name );
5822
5823 for( type = domain->typeList; type; type = type->next )
5824 {
5825 const char * description;
5826 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5827
5828 description = ServiceTypeDescription( type->name );
5829 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5830 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5831
5832 for( instance = type->instanceList; instance; instance = instance->next )
5833 {
5834 char * dst = textBuf;
5835 char * const lim = &textBuf[ countof( textBuf ) ];
5836 char ifname[ IF_NAMESIZE + 1 ];
5837
5838 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5839 if( instance->ifIndex == 0 )
5840 {
5841 SNPrintF_Add( &dst, lim, "the Internet" );
5842 }
5843 else if( if_indextoname( instance->ifIndex, ifname ) )
5844 {
5845 NetTransportType netType;
5846
5847 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5848 SNPrintF_Add( &dst, lim, "%s (%s)",
5849 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5850 ifname );
5851 }
5852 else
5853 {
5854 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5855 }
5856 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5857 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5858
5859 if( instance->hostname )
5860 {
5861 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5862 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5863 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5864 }
5865 else
5866 {
5867 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5868 }
5869
5870 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5871 {
5872 BrowseAllConnection * conn;
5873 BrowseAllConnection ** connPtr;
5874
5875 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5876 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5877
5878 conn = NULL;
5879 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5880 {
5881 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5882 {
5883 if( ( conn->port == instance->port ) &&
5884 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5885 }
5886 if( conn )
5887 {
5888 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5889 *connPtr = conn->next;
5890 conn->context = NULL;
5891 AsyncConnection_Forget( &conn->asyncCnx );
5892 }
5893 }
5894
5895 if( conn )
5896 {
5897 if( conn->status == kNoErr )
5898 {
5899 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5900 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5901 }
5902 else
5903 {
5904 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5905 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5906 }
5907 _BrowseAllConnectionRelease( conn );
5908 }
5909 else
5910 {
5911 FPrintF( stdout, " (no connection attempted)\n" );
5912 }
5913 }
5914
5915 FPrintF( stdout, "\n" );
5916 if( instance->txtLen == 0 ) continue;
5917
5918 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5919 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5920 if( instance->txtLen > 1 )
5921 {
5922 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5923 }
5924 else
5925 {
5926 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5927 }
5928 FPrintF( stdout, "\n" );
5929 }
5930 FPrintF( stdout, "\n" );
5931 }
5932 }
5933
5934 _BrowseAllContextFree( context );
5935 Exit( NULL );
5936 }
5937
5938 //===========================================================================================================================
5939 // _IsServiceTypeTCP
5940 //===========================================================================================================================
5941
5942 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5943 {
5944 OSStatus err;
5945 const uint8_t * secondLabel;
5946 uint8_t name[ kDomainNameLengthMax ];
5947
5948 err = DomainNameFromString( name, inServiceType, NULL );
5949 if( !err )
5950 {
5951 secondLabel = DomainNameGetNextLabel( name );
5952 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5953 }
5954 return( false );
5955 }
5956
5957 //===========================================================================================================================
5958 // GetNameInfoCmd
5959 //===========================================================================================================================
5960
5961 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5962 {
5963 CaseFlagStringify( NI_NUMERICSCOPE ),
5964 CaseFlagStringify( NI_DGRAM ),
5965 CaseFlagStringify( NI_NUMERICSERV ),
5966 CaseFlagStringify( NI_NAMEREQD ),
5967 CaseFlagStringify( NI_NUMERICHOST ),
5968 CaseFlagStringify( NI_NOFQDN ),
5969 { 0, NULL }
5970 };
5971
5972 static void GetNameInfoCmd( void )
5973 {
5974 OSStatus err;
5975 sockaddr_ip sip;
5976 size_t sockAddrLen;
5977 unsigned int flags;
5978 const FlagStringPair * pair;
5979 struct timeval now;
5980 char host[ NI_MAXHOST ];
5981 char serv[ NI_MAXSERV ];
5982
5983 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5984 check_noerr( err );
5985 if( err )
5986 {
5987 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5988 goto exit;
5989 }
5990
5991 flags = 0;
5992 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5993 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5994 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5995 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5996 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5997 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5998
5999 // Print prologue.
6000
6001 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
6002 FPrintF( stdout, "Flags: 0x%X < ", flags );
6003 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
6004 {
6005 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
6006 }
6007 FPrintF( stdout, ">\n" );
6008 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6009 FPrintF( stdout, "---\n" );
6010
6011 // Call getnameinfo().
6012
6013 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
6014 (int) flags );
6015 gettimeofday( &now, NULL );
6016 if( err )
6017 {
6018 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
6019 }
6020 else
6021 {
6022 FPrintF( stdout, "host: %s\n", host );
6023 FPrintF( stdout, "serv: %s\n", serv );
6024 }
6025 FPrintF( stdout, "---\n" );
6026 FPrintF( stdout, "End time: %{du:time}\n", &now );
6027
6028 exit:
6029 gExitCode = err ? 1 : 0;
6030 }
6031
6032 //===========================================================================================================================
6033 // GetAddrInfoStressCmd
6034 //===========================================================================================================================
6035
6036 typedef struct
6037 {
6038 DNSServiceRef mainRef;
6039 DNSServiceRef sdRef;
6040 DNSServiceFlags flags;
6041 unsigned int interfaceIndex;
6042 unsigned int connectionNumber;
6043 unsigned int requestCount;
6044 unsigned int requestCountMax;
6045 unsigned int requestCountLimit;
6046 unsigned int durationMinMs;
6047 unsigned int durationMaxMs;
6048
6049 } GAIStressContext;
6050
6051 static void GetAddrInfoStressEvent( void *inContext );
6052 static void DNSSD_API
6053 GetAddrInfoStressCallback(
6054 DNSServiceRef inSDRef,
6055 DNSServiceFlags inFlags,
6056 uint32_t inInterfaceIndex,
6057 DNSServiceErrorType inError,
6058 const char * inHostname,
6059 const struct sockaddr * inSockAddr,
6060 uint32_t inTTL,
6061 void * inContext );
6062
6063 static void GetAddrInfoStressCmd( void )
6064 {
6065 OSStatus err;
6066 GAIStressContext * context = NULL;
6067 int i;
6068 DNSServiceFlags flags;
6069 uint32_t ifIndex;
6070 char ifName[ kInterfaceNameBufLen ];
6071
6072 if( gGAIStress_TestDurationSecs < 0 )
6073 {
6074 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
6075 err = kParamErr;
6076 goto exit;
6077 }
6078 if( gGAIStress_ConnectionCount <= 0 )
6079 {
6080 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
6081 err = kParamErr;
6082 goto exit;
6083 }
6084 if( gGAIStress_DurationMinMs <= 0 )
6085 {
6086 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
6087 err = kParamErr;
6088 goto exit;
6089 }
6090 if( gGAIStress_DurationMaxMs <= 0 )
6091 {
6092 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
6093 err = kParamErr;
6094 goto exit;
6095 }
6096 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
6097 {
6098 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
6099 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
6100 err = kParamErr;
6101 goto exit;
6102 }
6103 if( gGAIStress_RequestCountMax <= 0 )
6104 {
6105 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
6106 err = kParamErr;
6107 goto exit;
6108 }
6109
6110 // Set flags.
6111
6112 flags = GetDNSSDFlagsFromOpts();
6113
6114 // Set interface index.
6115
6116 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6117 require_noerr_quiet( err, exit );
6118
6119 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
6120 {
6121 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
6122 require_action( context, exit, err = kNoMemoryErr );
6123
6124 context->flags = flags;
6125 context->interfaceIndex = ifIndex;
6126 context->connectionNumber = (unsigned int)( i + 1 );
6127 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
6128 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
6129 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
6130
6131 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
6132 context = NULL;
6133 }
6134
6135 if( gGAIStress_TestDurationSecs > 0 )
6136 {
6137 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
6138 }
6139
6140 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
6141 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
6142 FPrintF( stdout, "Test duration: " );
6143 if( gGAIStress_TestDurationSecs == 0 )
6144 {
6145 FPrintF( stdout, "∞\n" );
6146 }
6147 else
6148 {
6149 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
6150 }
6151 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
6152 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
6153 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
6154 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
6155 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
6156 FPrintF( stdout, "---\n" );
6157
6158 dispatch_main();
6159
6160 exit:
6161 FreeNullSafe( context );
6162 if( err ) exit( 1 );
6163 }
6164
6165 //===========================================================================================================================
6166 // GetAddrInfoStressEvent
6167 //===========================================================================================================================
6168
6169 #define kStressRandStrLen 5
6170
6171 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
6172
6173 static void GetAddrInfoStressEvent( void *inContext )
6174 {
6175 GAIStressContext * const context = (GAIStressContext *) inContext;
6176 OSStatus err;
6177 DNSServiceRef sdRef;
6178 unsigned int nextMs;
6179 char randomStr[ kStressRandStrLen + 1 ];
6180 char hostname[ kStressRandStrLen + 4 + 1 ];
6181 Boolean isConnectionNew = false;
6182 static Boolean printedHeader = false;
6183
6184 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
6185 {
6186 DNSServiceForget( &context->mainRef );
6187 context->sdRef = NULL;
6188 context->requestCount = 0;
6189 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
6190
6191 err = DNSServiceCreateConnection( &context->mainRef );
6192 require_noerr( err, exit );
6193
6194 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
6195 require_noerr( err, exit );
6196
6197 isConnectionNew = true;
6198 }
6199
6200 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
6201 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
6202
6203 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
6204
6205 if( !printedHeader )
6206 {
6207 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
6208 printedHeader = true;
6209 }
6210 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
6211 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
6212
6213 DNSServiceForget( &context->sdRef );
6214 sdRef = context->mainRef;
6215 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
6216 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
6217 require_noerr( err, exit );
6218 context->sdRef = sdRef;
6219
6220 context->requestCount++;
6221
6222 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
6223
6224 exit:
6225 if( err ) exit( 1 );
6226 }
6227
6228 //===========================================================================================================================
6229 // GetAddrInfoStressCallback
6230 //===========================================================================================================================
6231
6232 static void DNSSD_API
6233 GetAddrInfoStressCallback(
6234 DNSServiceRef inSDRef,
6235 DNSServiceFlags inFlags,
6236 uint32_t inInterfaceIndex,
6237 DNSServiceErrorType inError,
6238 const char * inHostname,
6239 const struct sockaddr * inSockAddr,
6240 uint32_t inTTL,
6241 void * inContext )
6242 {
6243 Unused( inSDRef );
6244 Unused( inFlags );
6245 Unused( inInterfaceIndex );
6246 Unused( inError );
6247 Unused( inHostname );
6248 Unused( inSockAddr );
6249 Unused( inTTL );
6250 Unused( inContext );
6251 }
6252
6253 //===========================================================================================================================
6254 // DNSQueryCmd
6255 //===========================================================================================================================
6256
6257 typedef struct
6258 {
6259 sockaddr_ip serverAddr;
6260 uint64_t sendTicks;
6261 uint8_t * msgPtr;
6262 size_t msgLen;
6263 size_t msgOffset;
6264 const char * name;
6265 dispatch_source_t readSource;
6266 SocketRef sock;
6267 int timeLimitSecs;
6268 uint16_t queryID;
6269 uint16_t type;
6270 Boolean haveTCPLen;
6271 Boolean useTCP;
6272 Boolean printRawRData; // True if RDATA results are not to be formatted.
6273 uint8_t msgBuf[ 512 ];
6274
6275 } DNSQueryContext;
6276
6277 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
6278 static void DNSQueryReadHandler( void *inContext );
6279 static void DNSQueryCancelHandler( void *inContext );
6280
6281 static void DNSQueryCmd( void )
6282 {
6283 OSStatus err;
6284 DNSQueryContext * context = NULL;
6285 uint8_t * msgPtr;
6286 size_t msgLen, sendLen;
6287
6288 // Check command parameters.
6289
6290 if( gDNSQuery_TimeLimitSecs < -1 )
6291 {
6292 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
6293 err = kParamErr;
6294 goto exit;
6295 }
6296 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
6297 {
6298 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
6299 err = kParamErr;
6300 goto exit;
6301 }
6302
6303 // Create context.
6304
6305 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
6306 require_action( context, exit, err = kNoMemoryErr );
6307
6308 context->name = gDNSQuery_Name;
6309 context->sock = kInvalidSocketRef;
6310 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
6311 context->queryID = (uint16_t) Random32();
6312 context->useTCP = gDNSQuery_UseTCP ? true : false;
6313 context->printRawRData = gDNSQuery_RawRData ? true : false;
6314
6315 #if( TARGET_OS_DARWIN )
6316 if( gDNSQuery_Server )
6317 #endif
6318 {
6319 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6320 require_noerr( err, exit );
6321 }
6322 #if( TARGET_OS_DARWIN )
6323 else
6324 {
6325 err = GetDefaultDNSServer( &context->serverAddr );
6326 require_noerr( err, exit );
6327 }
6328 #endif
6329 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
6330
6331 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
6332 require_noerr( err, exit );
6333
6334 // Write query message.
6335
6336 check_compile_time_code( sizeof( context->msgBuf ) >= ( 2 + kDNSQueryMessageMaxLen + sizeof( dns_fixed_fields_opt ) ) );
6337
6338 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
6339 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
6340 kDNSServiceClass_IN, &msgLen );
6341 require_noerr( err, exit );
6342 check( msgLen <= UINT16_MAX );
6343
6344 if( gDNSQuery_DNSSEC )
6345 {
6346 DNSHeader * const hdr = (DNSHeader *) msgPtr;
6347 dns_fixed_fields_opt * const opt = (dns_fixed_fields_opt *) &msgPtr[ msgLen ];
6348 unsigned int flags;
6349
6350 memset( opt, 0, sizeof( *opt ) );
6351 dns_fixed_fields_opt_set_type( opt, kDNSServiceType_OPT );
6352 dns_fixed_fields_opt_set_udp_payload_size( opt, 512 );
6353 dns_fixed_fields_opt_set_extended_flags( opt, kDNSExtendedFlag_DNSSECOK );
6354
6355 flags = DNSHeaderGetFlags( hdr ) | kDNSHeaderFlag_AuthenticData;
6356 DNSHeaderSetFlags( hdr, flags );
6357 DNSHeaderSetAdditionalCount( hdr, 1 );
6358 msgLen += sizeof( dns_fixed_fields_opt );
6359 }
6360 if( gDNSQuery_CheckingDisabled )
6361 {
6362 DNSHeader * const hdr = (DNSHeader *) msgPtr;
6363 unsigned int flags;
6364
6365 flags = DNSHeaderGetFlags( hdr ) | kDNSHeaderFlag_CheckingDisabled;
6366 DNSHeaderSetFlags( hdr, flags );
6367 }
6368 if( context->useTCP )
6369 {
6370 WriteBig16( context->msgBuf, msgLen );
6371 sendLen = 2 + msgLen;
6372 }
6373 else
6374 {
6375 sendLen = msgLen;
6376 }
6377
6378 DNSQueryPrintPrologue( context );
6379
6380 if( gDNSQuery_Verbose )
6381 {
6382 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}\n", msgPtr, msgLen );
6383 FPrintF( stdout, "---\n" );
6384 }
6385
6386 if( context->useTCP )
6387 {
6388 // Create TCP socket.
6389
6390 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
6391 err = map_socket_creation_errno( context->sock );
6392 require_noerr( err, exit );
6393
6394 err = SocketConnect( context->sock, &context->serverAddr, 5 );
6395 require_noerr( err, exit );
6396 }
6397 else
6398 {
6399 // Create UDP socket.
6400
6401 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
6402 require_noerr( err, exit );
6403 }
6404
6405 context->sendTicks = UpTicks();
6406 err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
6407 require_noerr( err, exit );
6408
6409 if( context->timeLimitSecs == 0 ) goto exit;
6410
6411 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
6412 &context->readSource );
6413 require_noerr( err, exit );
6414 dispatch_resume( context->readSource );
6415
6416 if( context->timeLimitSecs > 0 )
6417 {
6418 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6419 Exit );
6420 }
6421 dispatch_main();
6422
6423 exit:
6424 if( context )
6425 {
6426 dispatch_source_forget( &context->readSource );
6427 ForgetSocket( &context->sock );
6428 free( context );
6429 }
6430 if( err ) exit( 1 );
6431 }
6432
6433 //===========================================================================================================================
6434 // DNSQueryPrintPrologue
6435 //===========================================================================================================================
6436
6437 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
6438 {
6439 const int timeLimitSecs = inContext->timeLimitSecs;
6440
6441 FPrintF( stdout, "Name: %s\n", inContext->name );
6442 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
6443 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
6444 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
6445 FPrintF( stdout, "Time limit: " );
6446 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
6447 else FPrintF( stdout, "∞\n" );
6448 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6449 FPrintF( stdout, "---\n" );
6450 }
6451
6452 //===========================================================================================================================
6453 // DNSQueryReadHandler
6454 //===========================================================================================================================
6455
6456 static void DNSQueryReadHandler( void *inContext )
6457 {
6458 OSStatus err;
6459 struct timeval now;
6460 const uint64_t nowTicks = UpTicks();
6461 DNSQueryContext * const context = (DNSQueryContext *) inContext;
6462
6463 gettimeofday( &now, NULL );
6464
6465 if( context->useTCP )
6466 {
6467 if( !context->haveTCPLen )
6468 {
6469 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
6470 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6471 require_noerr( err, exit );
6472
6473 context->msgOffset = 0;
6474 context->msgLen = ReadBig16( context->msgBuf );
6475 context->haveTCPLen = true;
6476 if( context->msgLen <= sizeof( context->msgBuf ) )
6477 {
6478 context->msgPtr = context->msgBuf;
6479 }
6480 else
6481 {
6482 context->msgPtr = (uint8_t *) malloc( context->msgLen );
6483 require_action( context->msgPtr, exit, err = kNoMemoryErr );
6484 }
6485 }
6486
6487 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
6488 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6489 require_noerr( err, exit );
6490 context->msgOffset = 0;
6491 context->haveTCPLen = false;
6492 }
6493 else
6494 {
6495 sockaddr_ip fromAddr;
6496
6497 context->msgPtr = context->msgBuf;
6498 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
6499 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6500 require_noerr( err, exit );
6501
6502 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6503 }
6504
6505 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6506 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6507 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6508 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6509 if( context->printRawRData ) FPrintF( stdout, "%{du:rdnsmsg}\n", context->msgPtr, context->msgLen );
6510 else FPrintF( stdout, "%{du:dnsmsg}\n", context->msgPtr, context->msgLen );
6511
6512 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
6513 {
6514 Exit( kExitReason_ReceivedResponse );
6515 }
6516
6517 exit:
6518 if( err ) dispatch_source_forget( &context->readSource );
6519 }
6520
6521 //===========================================================================================================================
6522 // DNSQueryCancelHandler
6523 //===========================================================================================================================
6524
6525 static void DNSQueryCancelHandler( void *inContext )
6526 {
6527 DNSQueryContext * const context = (DNSQueryContext *) inContext;
6528
6529 check( !context->readSource );
6530 ForgetSocket( &context->sock );
6531 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
6532 free( context );
6533 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
6534 }
6535
6536 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
6537 //===========================================================================================================================
6538 // DNSCryptCmd
6539 //===========================================================================================================================
6540
6541 #define kDNSCryptPort 443
6542
6543 #define kDNSCryptMinPadLength 8
6544 #define kDNSCryptMaxPadLength 256
6545 #define kDNSCryptBlockSize 64
6546 #define kDNSCryptCertMinimumLength 124
6547 #define kDNSCryptClientMagicLength 8
6548 #define kDNSCryptResolverMagicLength 8
6549 #define kDNSCryptHalfNonceLength 12
6550 #define kDNSCryptCertMagicLength 4
6551
6552 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
6553
6554 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
6555 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
6556 {
6557 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
6558 };
6559
6560 typedef struct
6561 {
6562 uint8_t certMagic[ kDNSCryptCertMagicLength ];
6563 uint8_t esVersion[ 2 ];
6564 uint8_t minorVersion[ 2 ];
6565 uint8_t signature[ crypto_sign_BYTES ];
6566 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
6567 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6568 uint8_t serial[ 4 ];
6569 uint8_t startTime[ 4 ];
6570 uint8_t endTime[ 4 ];
6571 uint8_t extensions[ 1 ]; // Variably-sized extension data.
6572
6573 } DNSCryptCert;
6574
6575 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
6576
6577 typedef struct
6578 {
6579 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6580 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6581 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6582 uint8_t poly1305MAC[ 16 ];
6583
6584 } DNSCryptQueryHeader;
6585
6586 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
6587 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
6588 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
6589 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
6590
6591 typedef struct
6592 {
6593 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
6594 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6595 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
6596 uint8_t poly1305MAC[ 16 ];
6597
6598 } DNSCryptResponseHeader;
6599
6600 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
6601 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
6602 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
6603 sizeof( DNSCryptResponseHeader ) );
6604
6605 typedef struct
6606 {
6607 sockaddr_ip serverAddr;
6608 uint64_t sendTicks;
6609 const char * providerName;
6610 const char * qname;
6611 const uint8_t * certPtr;
6612 size_t certLen;
6613 dispatch_source_t readSource;
6614 size_t msgLen;
6615 int timeLimitSecs;
6616 uint16_t queryID;
6617 uint16_t qtype;
6618 Boolean printRawRData;
6619 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
6620 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
6621 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6622 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
6623 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
6624 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
6625 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
6626 uint8_t msgBuf[ 512 ];
6627
6628 } DNSCryptContext;
6629
6630 static void DNSCryptReceiveCertHandler( void *inContext );
6631 static void DNSCryptReceiveResponseHandler( void *inContext );
6632 static void DNSCryptProceed( void *inContext );
6633 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
6634 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
6635 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
6636 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
6637
6638 static void DNSCryptCmd( void )
6639 {
6640 OSStatus err;
6641 DNSCryptContext * context = NULL;
6642 size_t writtenBytes;
6643 size_t totalBytes;
6644 SocketContext * sockCtx;
6645 SocketRef sock = kInvalidSocketRef;
6646 const char * ptr;
6647
6648 // Check command parameters.
6649
6650 if( gDNSCrypt_TimeLimitSecs < -1 )
6651 {
6652 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
6653 err = kParamErr;
6654 goto exit;
6655 }
6656
6657 // Create context.
6658
6659 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
6660 require_action( context, exit, err = kNoMemoryErr );
6661
6662 context->providerName = gDNSCrypt_ProviderName;
6663 context->qname = gDNSCrypt_Name;
6664 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
6665 context->printRawRData = gDNSCrypt_RawRData ? true : false;
6666
6667 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
6668 require_noerr( err, exit );
6669
6670 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
6671 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
6672 if( err || ( *ptr != '\0' ) )
6673 {
6674 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
6675 goto exit;
6676 }
6677 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
6678 {
6679 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6680 totalBytes, sizeof( context->serverPublicSignKey ) );
6681 err = kSizeErr;
6682 goto exit;
6683 }
6684 check( writtenBytes == totalBytes );
6685
6686 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6687 require_noerr( err, exit );
6688 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
6689
6690 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
6691 require_noerr( err, exit );
6692
6693 // Write query message.
6694
6695 context->queryID = (uint16_t) Random32();
6696 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6697 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6698 require_noerr( err, exit );
6699
6700 // Create UDP socket.
6701
6702 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6703 require_noerr( err, exit );
6704
6705 // Send DNS query.
6706
6707 context->sendTicks = UpTicks();
6708 err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6709 require_noerr( err, exit );
6710
6711 sockCtx = SocketContextCreate( sock, context, &err );
6712 require_noerr( err, exit );
6713 sock = kInvalidSocketRef;
6714
6715 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6716 &context->readSource );
6717 if( err ) ForgetSocketContext( &sockCtx );
6718 require_noerr( err, exit );
6719
6720 dispatch_resume( context->readSource );
6721
6722 if( context->timeLimitSecs > 0 )
6723 {
6724 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6725 Exit );
6726 }
6727 dispatch_main();
6728
6729 exit:
6730 if( context ) free( context );
6731 ForgetSocket( &sock );
6732 if( err ) exit( 1 );
6733 }
6734
6735 //===========================================================================================================================
6736 // DNSCryptReceiveCertHandler
6737 //===========================================================================================================================
6738
6739 static void DNSCryptReceiveCertHandler( void *inContext )
6740 {
6741 OSStatus err;
6742 struct timeval now;
6743 const uint64_t nowTicks = UpTicks();
6744 SocketContext * const sockCtx = (SocketContext *) inContext;
6745 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6746 const DNSHeader * hdr;
6747 sockaddr_ip fromAddr;
6748 const uint8_t * ptr;
6749 const uint8_t * txtPtr;
6750 size_t txtLen;
6751 unsigned int answerCount, i;
6752 uint8_t targetName[ kDomainNameLengthMax ];
6753
6754 gettimeofday( &now, NULL );
6755
6756 dispatch_source_forget( &context->readSource );
6757
6758 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6759 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6760 require_noerr( err, exit );
6761 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6762
6763 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6764 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6765 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6766 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6767 if( context->printRawRData ) FPrintF( stdout, "%{du:rdnsmsg}\n", context->msgBuf, context->msgLen );
6768 else FPrintF( stdout, "%{du:dnsmsg}\n", context->msgBuf, context->msgLen );
6769
6770 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6771
6772 hdr = (DNSHeader *) context->msgBuf;
6773 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6774
6775 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6776 require_noerr( err, exit );
6777
6778 err = DomainNameFromString( targetName, context->providerName, NULL );
6779 require_noerr( err, exit );
6780
6781 answerCount = DNSHeaderGetAnswerCount( hdr );
6782 for( i = 0; i < answerCount; ++i )
6783 {
6784 uint16_t type;
6785 uint16_t class;
6786 uint8_t name[ kDomainNameLengthMax ];
6787
6788 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6789 &ptr );
6790 require_noerr( err, exit );
6791
6792 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6793 {
6794 break;
6795 }
6796 }
6797
6798 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6799 {
6800 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6801 err = kSizeErr;
6802 goto exit;
6803 }
6804 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6805 {
6806 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6807 err = kSizeErr;
6808 goto exit;
6809 }
6810
6811 context->certLen = txtPtr[ 0 ];
6812 context->certPtr = &txtPtr[ 1 ];
6813
6814 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6815
6816 exit:
6817 if( err ) Exit( NULL );
6818 }
6819
6820 //===========================================================================================================================
6821 // DNSCryptReceiveResponseHandler
6822 //===========================================================================================================================
6823
6824 static void DNSCryptReceiveResponseHandler( void *inContext )
6825 {
6826 OSStatus err;
6827 struct timeval now;
6828 const uint64_t nowTicks = UpTicks();
6829 SocketContext * const sockCtx = (SocketContext *) inContext;
6830 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6831 sockaddr_ip fromAddr;
6832 DNSCryptResponseHeader * hdr;
6833 const uint8_t * end;
6834 uint8_t * ciphertext;
6835 uint8_t * plaintext;
6836 const uint8_t * response;
6837 uint8_t nonce[ crypto_box_NONCEBYTES ];
6838
6839 gettimeofday( &now, NULL );
6840
6841 dispatch_source_forget( &context->readSource );
6842
6843 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6844 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6845 require_noerr( err, exit );
6846 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6847
6848 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6849 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6850 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6851 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6852
6853 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6854 {
6855 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6856 err = kSizeErr;
6857 goto exit;
6858 }
6859
6860 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6861
6862 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6863 {
6864 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6865 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6866 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6867 err = kValueErr;
6868 goto exit;
6869 }
6870
6871 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6872 {
6873 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6874 err = kValueErr;
6875 goto exit;
6876 }
6877
6878 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6879
6880 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6881 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6882
6883 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6884 check( plaintext == ciphertext );
6885
6886 end = context->msgBuf + context->msgLen;
6887
6888 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6889 require_noerr( err, exit );
6890
6891 response = plaintext + crypto_box_ZEROBYTES;
6892 if( context->printRawRData ) FPrintF( stdout, "%{du:rdnsmsg}\n", response, (size_t)( end - response ) );
6893 else FPrintF( stdout, "%{du:dnsmsg}\n", response, (size_t)( end - response ) );
6894 Exit( kExitReason_ReceivedResponse );
6895
6896 exit:
6897 if( err ) Exit( NULL );
6898 }
6899
6900 //===========================================================================================================================
6901 // DNSCryptProceed
6902 //===========================================================================================================================
6903
6904 static void DNSCryptProceed( void *inContext )
6905 {
6906 OSStatus err;
6907 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6908
6909 err = DNSCryptProcessCert( context );
6910 require_noerr_quiet( err, exit );
6911
6912 err = DNSCryptBuildQuery( context );
6913 require_noerr_quiet( err, exit );
6914
6915 err = DNSCryptSendQuery( context );
6916 require_noerr_quiet( err, exit );
6917
6918 exit:
6919 if( err ) Exit( NULL );
6920 }
6921
6922 //===========================================================================================================================
6923 // DNSCryptProcessCert
6924 //===========================================================================================================================
6925
6926 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6927 {
6928 OSStatus err;
6929 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6930 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6931 struct timeval now;
6932 time_t startTimeSecs, endTimeSecs;
6933 size_t signedLen;
6934 uint8_t * tempBuf;
6935 unsigned long long tempLen;
6936
6937 DNSCryptPrintCertificate( cert, inContext->certLen );
6938
6939 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6940 {
6941 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6942 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6943 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6944 err = kValueErr;
6945 goto exit;
6946 }
6947
6948 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6949 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6950
6951 gettimeofday( &now, NULL );
6952 if( now.tv_sec < startTimeSecs )
6953 {
6954 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6955 err = kDateErr;
6956 goto exit;
6957 }
6958 if( now.tv_sec >= endTimeSecs )
6959 {
6960 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6961 err = kDateErr;
6962 goto exit;
6963 }
6964
6965 signedLen = (size_t)( certEnd - cert->signature );
6966 tempBuf = (uint8_t *) malloc( signedLen );
6967 require_action( tempBuf, exit, err = kNoMemoryErr );
6968 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6969 free( tempBuf );
6970 if( err )
6971 {
6972 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6973 err = kAuthenticationErr;
6974 goto exit;
6975 }
6976
6977 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6978 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6979
6980 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6981 require_noerr( err, exit );
6982
6983 inContext->certPtr = NULL;
6984 inContext->certLen = 0;
6985 inContext->msgLen = 0;
6986
6987 exit:
6988 return( err );
6989 }
6990
6991 //===========================================================================================================================
6992 // DNSCryptBuildQuery
6993 //===========================================================================================================================
6994
6995 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6996
6997 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6998 {
6999 OSStatus err;
7000 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
7001 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
7002 size_t queryLen;
7003 size_t paddedQueryLen;
7004 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
7005 const uint8_t * padLimit;
7006 uint8_t nonce[ crypto_box_NONCEBYTES ];
7007
7008 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
7009
7010 inContext->queryID = (uint16_t) Random32();
7011 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
7012 inContext->qtype, kDNSServiceClass_IN, &queryLen );
7013 require_noerr( err, exit );
7014
7015 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
7016 if( padLimit > msgLimit ) padLimit = msgLimit;
7017
7018 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
7019 require_noerr( err, exit );
7020
7021 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
7022 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
7023 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
7024 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
7025
7026 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
7027 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
7028 require_noerr( err, exit );
7029
7030 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
7031 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
7032 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
7033
7034 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
7035
7036 exit:
7037 return( err );
7038 }
7039
7040 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
7041 {
7042 OSStatus err;
7043 size_t paddedLen;
7044
7045 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
7046
7047 paddedLen = inMsgLen + kDNSCryptMinPadLength +
7048 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
7049 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
7050 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
7051
7052 inMsgPtr[ inMsgLen ] = 0x80;
7053 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
7054
7055 if( outPaddedLen ) *outPaddedLen = paddedLen;
7056 err = kNoErr;
7057
7058 exit:
7059 return( err );
7060 }
7061
7062 //===========================================================================================================================
7063 // DNSCryptSendQuery
7064 //===========================================================================================================================
7065
7066 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
7067 {
7068 OSStatus err;
7069 SocketContext * sockCtx;
7070 SocketRef sock = kInvalidSocketRef;
7071
7072 check( inContext->msgLen > 0 );
7073 check( !inContext->readSource );
7074
7075 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
7076 require_noerr( err, exit );
7077
7078 inContext->sendTicks = UpTicks();
7079 err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
7080 require_noerr( err, exit );
7081
7082 sockCtx = SocketContextCreate( sock, inContext, &err );
7083 require_noerr( err, exit );
7084 sock = kInvalidSocketRef;
7085
7086 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
7087 &inContext->readSource );
7088 if( err ) ForgetSocketContext( &sockCtx );
7089 require_noerr( err, exit );
7090
7091 dispatch_resume( inContext->readSource );
7092
7093 exit:
7094 ForgetSocket( &sock );
7095 return( err );
7096 }
7097
7098 //===========================================================================================================================
7099 // DNSCryptPrintCertificate
7100 //===========================================================================================================================
7101
7102 #define kCertTimeStrBufLen 32
7103
7104 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
7105
7106 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
7107 {
7108 time_t startTime, endTime;
7109 int extLen;
7110 char timeBuf[ kCertTimeStrBufLen ];
7111
7112 check( inLen >= kDNSCryptCertMinimumLength );
7113
7114 startTime = (time_t) ReadBig32( inCert->startTime );
7115 endTime = (time_t) ReadBig32( inCert->endTime );
7116
7117 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
7118 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
7119 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
7120 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
7121 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
7122 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
7123 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
7124 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
7125 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
7126 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
7127 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
7128
7129 if( inLen > kDNSCryptCertMinimumLength )
7130 {
7131 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
7132 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
7133 }
7134 FPrintF( stdout, "\n" );
7135 }
7136
7137 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
7138 {
7139 struct tm * tm;
7140
7141 tm = localtime( &inTime );
7142 if( !tm )
7143 {
7144 dlogassert( "localtime() returned a NULL pointer.\n" );
7145 *inBuffer = '\0';
7146 }
7147 else
7148 {
7149 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
7150 }
7151
7152 return( inBuffer );
7153 }
7154
7155 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
7156
7157 //===========================================================================================================================
7158 // MDNSQueryCmd
7159 //===========================================================================================================================
7160
7161 typedef struct
7162 {
7163 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
7164 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
7165 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
7166 int localPort; // The port number to which the sockets are bound.
7167 int receiveSecs; // After send, the amount of time to spend receiving.
7168 uint32_t ifIndex; // Index of the interface over which to send the query.
7169 uint16_t qtype; // The type (QTYPE) of the record being queried.
7170 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
7171 Boolean allResponses; // True if all mDNS messages received should be printed.
7172 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
7173 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
7174 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
7175 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
7176 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
7177 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
7178
7179 } MDNSQueryContext;
7180
7181 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
7182 static void MDNSQueryReadHandler( void *inContext );
7183
7184 static void MDNSQueryCmd( void )
7185 {
7186 OSStatus err;
7187 MDNSQueryContext * context;
7188 SocketRef sockV4 = kInvalidSocketRef;
7189 SocketRef sockV6 = kInvalidSocketRef;
7190 ssize_t n;
7191 const char * ifname;
7192 size_t msgLen;
7193 unsigned int sendCount;
7194
7195 // Check command parameters.
7196
7197 if( gMDNSQuery_ReceiveSecs < -1 )
7198 {
7199 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
7200 err = kParamErr;
7201 goto exit;
7202 }
7203
7204 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
7205 require_action( context, exit, err = kNoMemoryErr );
7206
7207 context->qnameStr = gMDNSQuery_Name;
7208 context->receiveSecs = gMDNSQuery_ReceiveSecs;
7209 context->isQU = gMDNSQuery_IsQU ? true : false;
7210 context->allResponses = gMDNSQuery_AllResponses ? true : false;
7211 context->printRawRData = gMDNSQuery_RawRData ? true : false;
7212 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
7213 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
7214
7215 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
7216 require_noerr_quiet( err, exit );
7217
7218 ifname = if_indextoname( context->ifIndex, context->ifName );
7219 require_action( ifname, exit, err = kNameErr );
7220
7221 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
7222 require_noerr( err, exit );
7223
7224 // Set up IPv4 socket.
7225
7226 if( context->useIPv4 )
7227 {
7228 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
7229 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
7230 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
7231 require_noerr( err, exit );
7232 }
7233
7234 // Set up IPv6 socket.
7235
7236 if( context->useIPv6 )
7237 {
7238 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
7239 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
7240 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
7241 require_noerr( err, exit );
7242 }
7243
7244 // Craft mDNS query message.
7245
7246 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
7247 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
7248 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kMDNSClassUnicastResponseBit ) : kDNSServiceClass_IN,
7249 &msgLen );
7250 require_noerr( err, exit );
7251
7252 // Print prologue.
7253
7254 MDNSQueryPrintPrologue( context );
7255
7256 // Send mDNS query message.
7257
7258 sendCount = 0;
7259 if( IsValidSocket( sockV4 ) )
7260 {
7261 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
7262
7263 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
7264 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
7265 if( err )
7266 {
7267 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
7268 ForgetSocket( &sockV4 );
7269 }
7270 else
7271 {
7272 ++sendCount;
7273 }
7274 }
7275 if( IsValidSocket( sockV6 ) )
7276 {
7277 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
7278
7279 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
7280 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
7281 if( err )
7282 {
7283 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
7284 ForgetSocket( &sockV6 );
7285 }
7286 else
7287 {
7288 ++sendCount;
7289 }
7290 }
7291 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
7292
7293 // If there's no wait period after the send, then exit.
7294
7295 if( context->receiveSecs == 0 ) goto exit;
7296
7297 // Create dispatch read sources for socket(s).
7298
7299 if( IsValidSocket( sockV4 ) )
7300 {
7301 SocketContext * sockCtx;
7302
7303 sockCtx = SocketContextCreate( sockV4, context, &err );
7304 require_noerr( err, exit );
7305 sockV4 = kInvalidSocketRef;
7306
7307 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
7308 &context->readSourceV4 );
7309 if( err ) ForgetSocketContext( &sockCtx );
7310 require_noerr( err, exit );
7311
7312 dispatch_resume( context->readSourceV4 );
7313 }
7314
7315 if( IsValidSocket( sockV6 ) )
7316 {
7317 SocketContext * sockCtx;
7318
7319 sockCtx = SocketContextCreate( sockV6, context, &err );
7320 require_noerr( err, exit );
7321 sockV6 = kInvalidSocketRef;
7322
7323 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
7324 &context->readSourceV6 );
7325 if( err ) ForgetSocketContext( &sockCtx );
7326 require_noerr( err, exit );
7327
7328 dispatch_resume( context->readSourceV6 );
7329 }
7330
7331 if( context->receiveSecs > 0 )
7332 {
7333 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
7334 Exit );
7335 }
7336 dispatch_main();
7337
7338 exit:
7339 ForgetSocket( &sockV4 );
7340 ForgetSocket( &sockV6 );
7341 if( err ) exit( 1 );
7342 }
7343
7344 //===========================================================================================================================
7345 // MDNSColliderCmd
7346 //===========================================================================================================================
7347
7348 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
7349
7350 static void MDNSColliderCmd( void )
7351 {
7352 OSStatus err;
7353 MDNSColliderRef collider = NULL;
7354 uint8_t * rdataPtr = NULL;
7355 size_t rdataLen = 0;
7356 const char * ifname;
7357 uint32_t ifIndex;
7358 MDNSColliderProtocols protocols;
7359 uint16_t type;
7360 char ifName[ IF_NAMESIZE + 1 ];
7361 uint8_t name[ kDomainNameLengthMax ];
7362
7363 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
7364 require_noerr_quiet( err, exit );
7365
7366 ifname = if_indextoname( ifIndex, ifName );
7367 if( !ifname )
7368 {
7369 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
7370 err = kNameErr;
7371 goto exit;
7372 }
7373
7374 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
7375 if( err )
7376 {
7377 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
7378 goto exit;
7379 }
7380
7381 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
7382 require_noerr_quiet( err, exit );
7383
7384 if( gMDNSCollider_RecordData )
7385 {
7386 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
7387 require_noerr_quiet( err, exit );
7388 }
7389
7390 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
7391 require_noerr( err, exit );
7392
7393 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
7394 if( err )
7395 {
7396 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
7397 goto exit;
7398 }
7399
7400 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
7401 require_noerr( err, exit );
7402 ForgetMem( &rdataPtr );
7403
7404 protocols = kMDNSColliderProtocol_None;
7405 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
7406 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
7407 MDNSColliderSetProtocols( collider, protocols );
7408 MDNSColliderSetInterfaceIndex( collider, ifIndex );
7409 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
7410
7411 err = MDNSColliderStart( collider );
7412 require_noerr( err, exit );
7413
7414 dispatch_main();
7415
7416 exit:
7417 FreeNullSafe( rdataPtr );
7418 CFReleaseNullSafe( collider );
7419 if( err ) exit( 1 );
7420 }
7421
7422 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
7423 {
7424 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
7425
7426 CFRelease( collider );
7427 exit( inError ? 1 : 0 );
7428 }
7429
7430 //===========================================================================================================================
7431 // MDNSQueryPrintPrologue
7432 //===========================================================================================================================
7433
7434 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
7435 {
7436 const int receiveSecs = inContext->receiveSecs;
7437
7438 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
7439 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
7440 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
7441 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
7442 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
7443 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
7444 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
7445 FPrintF( stdout, "Receive duration: " );
7446 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
7447 else FPrintF( stdout, "∞\n" );
7448 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
7449 }
7450
7451 //===========================================================================================================================
7452 // MDNSQueryReadHandler
7453 //===========================================================================================================================
7454
7455 static void MDNSQueryReadHandler( void *inContext )
7456 {
7457 OSStatus err;
7458 struct timeval now;
7459 SocketContext * const sockCtx = (SocketContext *) inContext;
7460 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
7461 size_t msgLen;
7462 sockaddr_ip fromAddr;
7463 Boolean foundAnswer = false;
7464
7465 gettimeofday( &now, NULL );
7466
7467 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
7468 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
7469 require_noerr( err, exit );
7470
7471 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
7472 {
7473 const uint8_t * ptr;
7474 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
7475 unsigned int rrCount, i;
7476 uint16_t type, class;
7477 uint8_t name[ kDomainNameLengthMax ];
7478
7479 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
7480 require_noerr( err, exit );
7481
7482 if( context->qname[ 0 ] == 0 )
7483 {
7484 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
7485 require_noerr( err, exit );
7486 }
7487
7488 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
7489 for( i = 0; i < rrCount; ++i )
7490 {
7491 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
7492 require_noerr( err, exit );
7493
7494 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
7495 DomainNameEqual( name, context->qname ) )
7496 {
7497 foundAnswer = true;
7498 break;
7499 }
7500 }
7501 }
7502 if( context->allResponses || foundAnswer )
7503 {
7504 FPrintF( stdout, "---\n" );
7505 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
7506 FPrintF( stdout, "Source: %##a\n", &fromAddr );
7507 FPrintF( stdout, "Message size: %zu\n\n", msgLen );
7508 if( context->printRawRData ) FPrintF( stdout, "%#{du:rdnsmsg}\n", context->msgBuf, msgLen );
7509 else FPrintF( stdout, "%#{du:dnsmsg}\n", context->msgBuf, msgLen );
7510 }
7511
7512 exit:
7513 if( err ) exit( 1 );
7514 }
7515
7516 #if( TARGET_OS_DARWIN )
7517 //===========================================================================================================================
7518 // PIDToUUIDCmd
7519 //===========================================================================================================================
7520
7521 static void PIDToUUIDCmd( void )
7522 {
7523 OSStatus err;
7524 int n;
7525 struct proc_uniqidentifierinfo info;
7526
7527 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
7528 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
7529
7530 FPrintF( stdout, "%#U\n", info.p_uuid );
7531 err = kNoErr;
7532
7533 exit:
7534 if( err ) exit( 1 );
7535 }
7536 #endif
7537
7538 //===========================================================================================================================
7539 // DNSServerCmd
7540 //===========================================================================================================================
7541
7542 typedef struct
7543 {
7544 DNSServerRef server; // Reference to the DNS server.
7545 dispatch_queue_t queue; // Serial queue for server.
7546 sockaddr_ip * addrArray; // Server's addresses.
7547 size_t addrCount; // Count of server's addresses.
7548 dispatch_source_t sourceSigInt; // Dispatch source for SIGINT.
7549 dispatch_source_t sourceSigTerm; // Dispatch source for SIGTERM.
7550 const char * domainOverride; // If non-NULL, server is to use this domain instead of "d.test.".
7551 dispatch_semaphore_t doneSem; // Semaphore to signal when the server is done.
7552 OSStatus error; // Error encounted while running server.
7553 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
7554 #if( TARGET_OS_DARWIN )
7555 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
7556 pid_t followPID; // PID of process being followed, if any. If it exits, we exit.
7557 Boolean addedResolver; // True if a resolver entry was added to the system DNS settings.
7558 #endif
7559
7560 } DNSServerCmdContext;
7561
7562 static void _DNSServerCmdStart( void *inCtx );
7563 static void _DNSServerCmdStop( DNSServerCmdContext *inCmd, OSStatus inError );
7564 #if( TARGET_OS_DARWIN )
7565 static OSStatus _DNSServerCmdAddExtraLoopbackAddrs( sockaddr_ip *inAddrArray, size_t inAddrCount, uint16_t inPort );
7566 #endif
7567 static void _DNSServerCmdContextFree( DNSServerCmdContext *inCmd );
7568 static void _DNSServerCmdServerStartHandler( const sockaddr_ip *inAddrArray, size_t inAddrCount, void *inCtx );
7569 static void _DNSServerCmdServerStopHandler( OSStatus inError, void *inCtx );
7570 static void _DNSServerCmdSIGINTHandler( void *inCtx );
7571 static void _DNSServerCmdSIGTERMHandler( void *inCtx );
7572 static void _DNSServerCmdShutdown( DNSServerCmdContext *inCtx, int inSignal );
7573 #if( TARGET_OS_DARWIN )
7574 static void _DNSServerCmdFollowedProcessHandler( void *inCtx );
7575 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, const sockaddr_ip *inAddrArray, size_t inAddrCount );
7576 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
7577 #endif
7578
7579 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
7580 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
7581
7582 static void DNSServerCmd( void )
7583 {
7584 OSStatus err;
7585 DNSServerCmdContext * cmd = NULL;
7586 sockaddr_ip * sip;
7587 size_t addrCount;
7588 Boolean listenOnV4, listenOnV6;
7589 #if( TARGET_OS_DARWIN )
7590 size_t extraLoopbackV6Count;
7591 #endif
7592
7593 // Check command arguments.
7594
7595 if( gDNSServer_Foreground ) LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
7596 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
7597 require_noerr_quiet( err, exit );
7598
7599 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
7600 require_noerr_quiet( err, exit );
7601
7602 err = CheckIntegerArgument( gDNSServer_Port, "port number", 0, UINT16_MAX );
7603 require_noerr_quiet( err, exit );
7604
7605 listenOnV4 = ( gDNSServer_ListenOnV4 || !gDNSServer_ListenOnV6 ) ? true : false;
7606 listenOnV6 = ( gDNSServer_ListenOnV6 || !gDNSServer_ListenOnV4 ) ? true : false;
7607 #if( TARGET_OS_DARWIN )
7608 if( gDNSServer_LoopbackOnly && listenOnV6 )
7609 {
7610 err = CheckIntegerArgument( gDNSServer_ExtraV6Count, "extra IPv6", 0, 100 );
7611 require_noerr_quiet( err, exit );
7612 extraLoopbackV6Count = (size_t) gDNSServer_ExtraV6Count;
7613 if( extraLoopbackV6Count > 0 )
7614 {
7615 err = CheckRootUser();
7616 require_noerr_quiet( err, exit );
7617 }
7618 }
7619 else
7620 {
7621 extraLoopbackV6Count = 0;
7622 }
7623 #endif
7624 cmd = (DNSServerCmdContext *) calloc( 1, sizeof( *cmd ) );
7625 require_action( cmd, exit, err = kNoMemoryErr );
7626
7627 cmd->domainOverride = gDNSServer_DomainOverride;
7628 cmd->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
7629 #if( TARGET_OS_DARWIN )
7630 cmd->followPID = -1;
7631 if( gDNSServer_FollowPID )
7632 {
7633 cmd->followPID = _StringToPID( gDNSServer_FollowPID, &err );
7634 if( err || ( cmd->followPID < 0 ) )
7635 {
7636 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
7637 err = kParamErr;
7638 goto exit;
7639 }
7640 }
7641 #endif
7642
7643 // Set up IP addresses.
7644
7645 if( listenOnV4 ) ++cmd->addrCount;
7646 if( listenOnV6 ) ++cmd->addrCount;
7647 #if( TARGET_OS_DARWIN )
7648 cmd->addrCount += extraLoopbackV6Count;
7649 #endif
7650 check( cmd->addrCount > 0 );
7651 cmd->addrArray = (sockaddr_ip *) calloc( cmd->addrCount, sizeof( *cmd->addrArray ) );
7652 require_action( cmd->addrArray, exit, err = kNoMemoryErr );
7653
7654 addrCount = 0;
7655 if( listenOnV4 )
7656 {
7657 sip = &cmd->addrArray[ addrCount++ ];
7658 _SockAddrInitIPv4( &sip->v4, cmd->loopbackOnly ? INADDR_LOOPBACK : INADDR_ANY, (uint16_t) gDNSServer_Port );
7659 }
7660 if( listenOnV6 )
7661 {
7662 const struct in6_addr * const addr = cmd->loopbackOnly ? &in6addr_loopback : &in6addr_any;
7663
7664 sip = &cmd->addrArray[ addrCount++ ];
7665 _SockAddrInitIPv6( &sip->v6, addr->s6_addr, 0, (uint16_t) gDNSServer_Port );
7666 }
7667 #if( TARGET_OS_DARWIN )
7668 if( extraLoopbackV6Count > 0 )
7669 {
7670 err = _DNSServerCmdAddExtraLoopbackAddrs( &cmd->addrArray[ addrCount ], extraLoopbackV6Count,
7671 (uint16_t) gDNSServer_Port );
7672 require_noerr( err, exit );
7673 addrCount += extraLoopbackV6Count;
7674 }
7675 #endif
7676 check( addrCount == cmd->addrCount );
7677
7678 // Start command.
7679
7680 cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.server-command", DISPATCH_QUEUE_SERIAL );
7681 require_action( cmd->queue, exit, err = kNoResourcesErr );
7682
7683 cmd->doneSem = dispatch_semaphore_create( 0 );
7684 require_action( cmd->doneSem, exit, err = kNoResourcesErr );
7685
7686 dispatch_async_f( cmd->queue, cmd, _DNSServerCmdStart );
7687 dispatch_semaphore_wait( cmd->doneSem, DISPATCH_TIME_FOREVER );
7688
7689 exit:
7690 if( err ) FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
7691 if( cmd ) _DNSServerCmdContextFree( cmd );
7692 gExitCode = err ? 1 : 0;
7693 }
7694
7695 //===========================================================================================================================
7696
7697 static void _DNSServerCmdStart( void *inCtx )
7698 {
7699 OSStatus err;
7700 DNSServerCmdContext * const cmd = (DNSServerCmdContext *) inCtx;
7701 size_t i;
7702
7703 signal( SIGINT, SIG_IGN );
7704 err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _DNSServerCmdSIGINTHandler, cmd, &cmd->sourceSigInt );
7705 require_noerr( err, exit );
7706 dispatch_resume( cmd->sourceSigInt );
7707
7708 signal( SIGTERM, SIG_IGN );
7709 err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _DNSServerCmdSIGTERMHandler, cmd, &cmd->sourceSigTerm );
7710 require_noerr( err, exit );
7711 dispatch_resume( cmd->sourceSigTerm );
7712
7713 #if( TARGET_OS_DARWIN )
7714 if( cmd->followPID >= 0 )
7715 {
7716 err = DispatchProcessMonitorCreate( cmd->followPID, DISPATCH_PROC_EXIT, cmd->queue,
7717 _DNSServerCmdFollowedProcessHandler, NULL, cmd, &cmd->processMonitor );
7718 require_noerr( err, exit );
7719 dispatch_resume( cmd->processMonitor );
7720 }
7721 #endif
7722 err = _DNSServerCreate( cmd->queue, _DNSServerCmdServerStartHandler, _DNSServerCmdServerStopHandler, cmd,
7723 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, cmd->addrArray, cmd->addrCount,
7724 cmd->domainOverride, gDNSServer_BadUDPMode ? true : false, &cmd->server );
7725 require_noerr( err, exit );
7726
7727 for( i = 0; i < gDNSServer_IgnoredQTypesCount; ++i )
7728 {
7729 uint16_t qtype;
7730
7731 err = RecordTypeFromArgString( gDNSServer_IgnoredQTypes[ i ], &qtype );
7732 require_noerr( err, exit );
7733
7734 err = _DNSServerSetIgnoredQType( cmd->server, qtype );
7735 require_noerr( err, exit );
7736 }
7737 _DNSServerStart( cmd->server );
7738
7739 exit:
7740 if( err ) _DNSServerCmdStop( cmd, err );
7741 }
7742
7743 //===========================================================================================================================
7744
7745 static void _DNSServerCmdStop( DNSServerCmdContext *inCmd, OSStatus inError )
7746 {
7747 if( !inCmd->error ) inCmd->error = inError;
7748 check( !inCmd->server );
7749 dispatch_source_forget( &inCmd->sourceSigInt );
7750 dispatch_source_forget( &inCmd->sourceSigTerm );
7751 #if( TARGET_OS_DARWIN )
7752 dispatch_source_forget( &inCmd->processMonitor );
7753 #endif
7754 dispatch_semaphore_signal( inCmd->doneSem );
7755 }
7756
7757 #if( TARGET_OS_DARWIN )
7758 //===========================================================================================================================
7759
7760 static OSStatus _DNSServerCmdAddExtraLoopbackAddrs( sockaddr_ip *inAddrArray, size_t inAddrCount, uint16_t inPort )
7761 {
7762 OSStatus err;
7763 uint8_t addrV6[ 16 ];
7764 size_t i;
7765
7766 check_compile_time_code( sizeof( kExtraLoopbackIPv6Prefix ) == 8 );
7767 memcpy( addrV6, kExtraLoopbackIPv6Prefix, 8 ); // 64-bit prefix
7768 RandomBytes( &addrV6[ 8 ], 4 ); // 32-bit random
7769 WriteBig32( &addrV6[ 12 ], 2 ); // 16-bit base offset starting at 2
7770 for( i = 0; i < inAddrCount; ++i )
7771 {
7772 struct sockaddr_in6 * const sin6 = &inAddrArray[ i ].v6;
7773
7774 err = _InterfaceIPv6AddressAdd( "lo0", addrV6, kExtraLoopbackIPv6PrefixBitLen );
7775 require_noerr( err, exit );
7776
7777 _SockAddrInitIPv6( sin6, addrV6, 0, inPort );
7778 BigEndianIntegerIncrement( addrV6, sizeof( addrV6 ) );
7779 }
7780 err = kNoErr;
7781
7782 exit:
7783 return( err );
7784 }
7785 #endif
7786
7787 //===========================================================================================================================
7788
7789 static void _DNSServerCmdContextFree( DNSServerCmdContext *inCmd )
7790 {
7791 #if( TARGET_OS_DARWIN )
7792 size_t i;
7793 #endif
7794
7795 check( !inCmd->server );
7796 check( !inCmd->sourceSigInt );
7797 check( !inCmd->sourceSigTerm );
7798 #if( TARGET_OS_DARWIN )
7799 check( !inCmd->processMonitor );
7800 for( i = 0; i < inCmd->addrCount; ++i )
7801 {
7802 OSStatus err;
7803 const struct sockaddr_in6 * const sin6 = &inCmd->addrArray[ i ].v6;
7804 int cmp;
7805
7806 if( sin6->sin6_family != AF_INET6 ) continue;
7807 cmp = memcmp( sin6->sin6_addr.s6_addr, kExtraLoopbackIPv6Prefix, sizeof( kExtraLoopbackIPv6Prefix ) );
7808 if( cmp != 0 ) continue;
7809 err = _InterfaceIPv6AddressRemove( "lo0", sin6->sin6_addr.s6_addr );
7810 check_noerr( err );
7811 }
7812 #endif
7813 dispatch_forget( &inCmd->queue );
7814 dispatch_forget( &inCmd->doneSem );
7815 ForgetMem( &inCmd->addrArray );
7816 free( inCmd );
7817 }
7818
7819 //===========================================================================================================================
7820
7821 static void _DNSServerCmdServerStartHandler( const sockaddr_ip *inServerArray, size_t inServerCount, void *inCtx )
7822 {
7823 #if( TARGET_OS_DARWIN )
7824 OSStatus err;
7825 DNSServerCmdContext * const cmd = (DNSServerCmdContext *) inCtx;
7826 const char * const domain = cmd->domainOverride ? cmd->domainOverride : "d.test.";
7827
7828 if( cmd->loopbackOnly )
7829 {
7830 err = _DNSServerCmdLoopbackResolverAdd( domain, inServerArray, inServerCount );
7831 if( err )
7832 {
7833 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"%s\" domain: %#m\n",
7834 domain, err );
7835 cmd->error = err;
7836 DNSServerForget( &cmd->server );
7837 }
7838 else
7839 {
7840 cmd->addedResolver = true;
7841 }
7842 }
7843 #else
7844 Unused( inServerArray );
7845 Unused( inServerCount );
7846 Unused( inCtx );
7847 #endif
7848 }
7849
7850 //===========================================================================================================================
7851
7852 static void _DNSServerCmdServerStopHandler( OSStatus inError, void *inCtx )
7853 {
7854 DNSServerCmdContext * const cmd = (DNSServerCmdContext *) inCtx;
7855
7856 if( inError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", inError );
7857 #if( TARGET_OS_DARWIN )
7858 if( cmd->addedResolver )
7859 {
7860 OSStatus err;
7861 err = _DNSServerCmdLoopbackResolverRemove();
7862 if( err ) ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7863 if( !err ) cmd->addedResolver = false;
7864 }
7865 #endif
7866 _DNSServerCmdStop( cmd, cmd->error ? cmd->error : inError );
7867 }
7868
7869 //===========================================================================================================================
7870
7871 static void _DNSServerCmdSIGINTHandler( void *inCtx )
7872 {
7873 _DNSServerCmdShutdown( (DNSServerCmdContext *) inCtx, SIGINT );
7874 }
7875
7876 //===========================================================================================================================
7877
7878 static void _DNSServerCmdSIGTERMHandler( void *inCtx )
7879 {
7880 _DNSServerCmdShutdown( (DNSServerCmdContext *) inCtx, SIGTERM );
7881 }
7882
7883 //===========================================================================================================================
7884
7885 static void _DNSServerCmdShutdown( DNSServerCmdContext *inCmd, int inSignal )
7886 {
7887 dispatch_source_forget( &inCmd->sourceSigInt );
7888 dispatch_source_forget( &inCmd->sourceSigTerm );
7889 #if( TARGET_OS_DARWIN )
7890 dispatch_source_forget( &inCmd->processMonitor );
7891 if( inSignal == 0 )
7892 {
7893 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inCmd->followPID );
7894 }
7895 else
7896 #endif
7897 {
7898 const char * sigName;
7899
7900 switch( inSignal )
7901 {
7902 case SIGINT: sigName = "SIGINT"; break;
7903 case SIGTERM: sigName = "SIGTERM"; break;
7904 default: sigName = "???"; break;
7905 }
7906 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, sigName );
7907 }
7908 DNSServerForget( &inCmd->server );
7909 }
7910
7911 #if( TARGET_OS_DARWIN )
7912 //===========================================================================================================================
7913
7914 static void _DNSServerCmdFollowedProcessHandler( void *inCtx )
7915 {
7916 DNSServerCmdContext * const cmd = (DNSServerCmdContext *) inCtx;
7917
7918 if( dispatch_source_get_data( cmd->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( cmd, 0 );
7919 }
7920 //===========================================================================================================================
7921
7922 #define kDNSServerServiceID CFSTR( "com.apple.dnssdutil.server" )
7923
7924 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, const sockaddr_ip *inAddrArray, size_t inAddrCount )
7925 {
7926 OSStatus err;
7927 CFPropertyListRef plist = NULL;
7928 SCDynamicStoreRef store = NULL;
7929 CFStringRef key = NULL;
7930 CFMutableArrayRef addresses;
7931 size_t i;
7932 Boolean ok;
7933 char dnssecDomainStr[ kDNSServiceMaxDomainName ];
7934
7935 require_action_quiet( inAddrCount > 0, exit, err = kCountErr );
7936
7937 err = DomainNameToString( kDNSServerDomain_DNSSEC, NULL, dnssecDomainStr, NULL );
7938 require_noerr( err, exit );
7939
7940 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7941 "{"
7942 "%kO=" // Domain array.
7943 "["
7944 "%s" // Non-DNSSEC domain.
7945 "%s" // DNSSEC domain.
7946 "%O" // Reverse IPv4 domain.
7947 "%O" // Reverse IPv6 domain.
7948 "]"
7949 "%kO=[%@]" // Server IP addresses.
7950 "%kO=%i" // Port number.
7951 "%kO=%O" // Interface name.
7952 "%kO=%O" // Service ID.
7953 "}",
7954 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7955 dnssecDomainStr,
7956 CFSTR( kDNSServerReverseIPv4DomainStr ),
7957 CFSTR( kDNSServerReverseIPv6DomainStr ),
7958 kSCPropNetDNSServerAddresses, &addresses,
7959 kSCPropNetDNSServerPort, SockAddrGetPort( inAddrArray ),
7960 kSCPropInterfaceName, CFSTR( "lo0" ),
7961 kSCPropNetDNSConfirmedServiceID, kDNSServerServiceID );
7962 require_noerr( err, exit );
7963
7964 for( i = 0; i < inAddrCount; ++i )
7965 {
7966 sockaddr_ip sip;
7967
7968 SockAddrCopy( &inAddrArray[ i ], &sip );
7969 SockAddrSetPort( &sip, 0 );
7970 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, addresses, "%##a", &sip );
7971 require_noerr( err, exit );
7972 }
7973 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7974 err = map_scerror( store );
7975 require_noerr( err, exit );
7976
7977 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, kDNSServerServiceID, kSCEntNetDNS );
7978 require_action( key, exit, err = kUnknownErr );
7979
7980 ok = SCDynamicStoreSetValue( store, key, plist );
7981 require_action( ok, exit, err = kUnknownErr );
7982
7983 exit:
7984 CFReleaseNullSafe( plist );
7985 CFReleaseNullSafe( store );
7986 CFReleaseNullSafe( key );
7987 return( err );
7988 }
7989
7990 //===========================================================================================================================
7991
7992 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7993 {
7994 OSStatus err;
7995 SCDynamicStoreRef store;
7996 CFStringRef key = NULL;
7997 Boolean success;
7998
7999 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
8000 err = map_scerror( store );
8001 require_noerr( err, exit );
8002
8003 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, kDNSServerServiceID, kSCEntNetDNS );
8004 require_action( key, exit, err = kUnknownErr );
8005
8006 success = SCDynamicStoreRemoveValue( store, key );
8007 require_action( success, exit, err = kUnknownErr );
8008
8009 exit:
8010 CFReleaseNullSafe( store );
8011 CFReleaseNullSafe( key );
8012 return( err );
8013 }
8014 #endif // TARGET_OS_DARWIN
8015
8016 //===========================================================================================================================
8017
8018 typedef struct DNSServerConnectionPrivate * DNSServerConnectionRef;
8019
8020 typedef struct DNSServerDelayedResponse DNSServerDelayedResponse;
8021 struct DNSServerDelayedResponse
8022 {
8023 DNSServerDelayedResponse * next; // Next delayed response in list.
8024 sockaddr_ip client; // Destination address.
8025 uint64_t dueTicks; // Time, in ticks, when send is due.
8026 uint8_t * msgPtr; // Response message pointer.
8027 size_t msgLen; // Response message length.
8028 size_t index; // Address index.
8029 SocketRef sock; // Socket to use for send.
8030 };
8031
8032 struct DNSServerPrivate
8033 {
8034 CFRuntimeBase base; // CF object base.
8035 uint8_t * domain; // Parent domain of server's resource records. (malloc'd)
8036 dispatch_queue_t queue; // Queue for DNS server's events.
8037 sockaddr_ip * addrArray; // Array of addresses to listen on.
8038 size_t addrCount; // Number of addresses to listen on.
8039 dispatch_source_t * readSourceArrayUDP; // Array of read sources for UDP sockets.
8040 dispatch_source_t * readSourceArrayTCP; // Array of read sources for TCP listening sockets.
8041 DNSServerConnectionRef connectionList; // List of TCP connections.
8042 dispatch_source_t connectionTimer; // Timer for idle connections.
8043 DNSServerStartHandler_f startHandler; // User's activation handler.
8044 DNSServerStopHandler_f stopHandler; // User's invalidation handler.
8045 void * userContext; // User's handler context.
8046 DNSServerDelayedResponse * responseList; // List of delayed UDP responses.
8047 dispatch_source_t responseTimer; // Timer for when to send next delayed response.
8048 int * ignoredQTypes; // Array of QTYPEs to ignore.
8049 size_t ignoredQTypeCount; // Number of QTYPEs to ignore.
8050 unsigned int responseDelayMs; // Response delay in milliseconds.
8051 uint32_t defaultTTL; // Default TTL for resource records.
8052 uint32_t serial; // Serial number for SOA record.
8053 OSStatus stopErr; // The error, if any, that caused the server to stop.
8054 Boolean started; // True if the server was started.
8055 Boolean stopped; // True if the server was stopped.
8056 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
8057 };
8058
8059 static void _DNSServerUDPReadHandler( void *inContext );
8060 static OSStatus
8061 _DNSServerScheduleDelayedResponse(
8062 DNSServerRef inServer,
8063 SocketRef inSock,
8064 const struct sockaddr * inDestAddr,
8065 uint8_t * inMsgPtr,
8066 size_t inMsgLen,
8067 size_t inIndex );
8068 static void _DNSServerDelayedResponseFree( DNSServerDelayedResponse *inResponse );
8069 static void _DNSServerDelayedResponseListFree( DNSServerDelayedResponse *inList );
8070 static void _DNSServerTCPAcceptHandler( void *inContext );
8071 static void _DNSServerConnectionTimerHandler( void *inContext );
8072 static void _DNSServerResetConnectionTimerMs( DNSServerRef me, uint64_t inTimeoutMs );
8073 static OSStatus
8074 _DNSServerAnswerQuery(
8075 DNSServerRef inServer,
8076 const uint8_t * inMsgPtr,
8077 size_t inMsgLen,
8078 size_t inIndex,
8079 Boolean inForTCP,
8080 uint8_t ** outResponsePtr,
8081 size_t * outResponseLen );
8082
8083 #define _DNSServerAnswerQueryForUDP( SERVER, QUERY_PTR, QUERY_LEN, INDEX, RESPONSE_PTR, RESPONSE_LEN ) \
8084 _DNSServerAnswerQuery( SERVER, QUERY_PTR, QUERY_LEN, INDEX, false, RESPONSE_PTR, RESPONSE_LEN )
8085
8086 #define _DNSServerAnswerQueryForTCP( SERVER, QUERY_PTR, QUERY_LEN, INDEX, RESPONSE_PTR, RESPONSE_LEN ) \
8087 _DNSServerAnswerQuery( SERVER, QUERY_PTR, QUERY_LEN, INDEX, true, RESPONSE_PTR, RESPONSE_LEN )
8088
8089 CF_CLASS_DEFINE( DNSServer );
8090
8091 struct DNSServerConnectionPrivate
8092 {
8093 CFRuntimeBase base; // CF object base.
8094 DNSServerConnectionRef next; // Next connection in list.
8095 DNSServerRef server; // Back pointer to server object.
8096 sockaddr_ip local; // TCP connection's local address.
8097 sockaddr_ip remote; // TCP connection's remote address.
8098 size_t index; // Sever address index.
8099 uint64_t expirationTicks; // Expiration time in ticks. Renewed upon receiving a complete query.
8100 dispatch_source_t readSource; // Dispatch read source for TCP connection.
8101 dispatch_source_t writeSource; // Dispatch write source for TCP connection.
8102 size_t offset; // Offset into receive buffer.
8103 void * msgPtr; // Pointer to dynamically allocated message buffer.
8104 size_t msgLen; // Length of message buffer.
8105 iovec_t iov[ 2 ]; // IO vector for writing response message.
8106 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8107 int iovCount; // Vector count for SocketWriteData().
8108 Boolean readSuspended; // True if the read source is currently suspended.
8109 Boolean writeSuspended; // True if the write source is currently suspended.
8110 Boolean haveLen; // True if currently receiving message instead of message length.
8111 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8112 };
8113
8114 static CFTypeID DNSServerConnectionGetTypeID( void );
8115 static OSStatus
8116 _DNSServerConnectionCreate(
8117 DNSServerRef inServer,
8118 const struct sockaddr * inLocal,
8119 const struct sockaddr * inRemote,
8120 size_t inIndex,
8121 DNSServerConnectionRef * outCnx );
8122 static OSStatus _DNSServerConnectionStart( DNSServerConnectionRef inCnx, SocketRef inSock );
8123 static void _DNSServerConnectionStop( DNSServerConnectionRef inCnx, Boolean inRemoveFromList );
8124 static void _DNSServerConnectionReadHandler( void *inContext );
8125 static void _DNSServerConnectionWriteHandler( void *inContext );
8126 static void _DNSServerConnectionRenewExpiration( DNSServerConnectionRef inCnx );
8127
8128 CF_CLASS_DEFINE( DNSServerConnection );
8129
8130 static OSStatus
8131 _DNSServerCreate(
8132 dispatch_queue_t inQueue,
8133 DNSServerStartHandler_f inStartHandler,
8134 DNSServerStopHandler_f inStopHandler,
8135 void * inUserContext,
8136 unsigned int inResponseDelayMs,
8137 uint32_t inDefaultTTL,
8138 const sockaddr_ip * inAddrArray,
8139 size_t inAddrCount,
8140 const char * inDomain,
8141 Boolean inBadUDPMode,
8142 DNSServerRef * outServer )
8143 {
8144 OSStatus err;
8145 DNSServerRef obj = NULL;
8146
8147 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
8148
8149 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
8150
8151 ReplaceDispatchQueue( &obj->queue, inQueue );
8152 obj->startHandler = inStartHandler;
8153 obj->stopHandler = inStopHandler;
8154 obj->userContext = inUserContext;
8155 obj->responseDelayMs = inResponseDelayMs;
8156 obj->defaultTTL = inDefaultTTL;
8157 obj->badUDPMode = inBadUDPMode;
8158 obj->addrCount = inAddrCount;
8159
8160 obj->addrArray = (sockaddr_ip *) _memdup( inAddrArray, obj->addrCount * sizeof( *obj->addrArray ) );
8161 require_action( obj->addrArray, exit, err = kNoMemoryErr );
8162
8163 obj->readSourceArrayUDP = (dispatch_source_t *) calloc( obj->addrCount, sizeof( *obj->readSourceArrayUDP ) );
8164 require_action( obj->readSourceArrayUDP, exit, err = kNoMemoryErr );
8165
8166 obj->readSourceArrayTCP = (dispatch_source_t *) calloc( obj->addrCount, sizeof( *obj->readSourceArrayTCP ) );
8167 require_action( obj->readSourceArrayTCP, exit, err = kNoMemoryErr );
8168
8169 if( inDomain )
8170 {
8171 err = StringToDomainName( inDomain, &obj->domain, NULL );
8172 require_noerr_quiet( err, exit );
8173 }
8174 else
8175 {
8176 err = DomainNameDup( kDNSServerDomain_Default, &obj->domain, NULL );
8177 require_noerr_quiet( err, exit );
8178 }
8179 *outServer = obj;
8180 obj = NULL;
8181 err = kNoErr;
8182
8183 exit:
8184 CFReleaseNullSafe( obj );
8185 return( err );
8186 }
8187
8188 //===========================================================================================================================
8189
8190 static void _DNSServerFinalize( CFTypeRef inObj )
8191 {
8192 DNSServerRef const me = (DNSServerRef) inObj;
8193 size_t i;
8194
8195 check( !me->responseTimer );
8196 check( !me->connectionList );
8197 check( !me->connectionTimer );
8198 ForgetMem( &me->addrArray );
8199 if( me->readSourceArrayUDP )
8200 {
8201 for( i = 0; i < me->addrCount; ++i ) check( !me->readSourceArrayUDP[ i ] );
8202 ForgetMem( &me->readSourceArrayUDP );
8203 }
8204 if( me->readSourceArrayTCP )
8205 {
8206 for( i = 0; i < me->addrCount; ++i ) check( !me->readSourceArrayTCP[ i ] );
8207 ForgetMem( &me->readSourceArrayTCP );
8208 }
8209 ForgetMem( &me->domain );
8210 dispatch_forget( &me->queue );
8211 ForgetMem( &me->ignoredQTypes );
8212 }
8213
8214 //===========================================================================================================================
8215
8216 static OSStatus _DNSServerSetIgnoredQType( DNSServerRef me, int inQType )
8217 {
8218 size_t newCount;
8219 int * mem;
8220
8221 newCount = me->ignoredQTypeCount + 1;
8222 require_return_value( newCount <= SIZE_MAX / sizeof( int ), kSizeErr );
8223
8224 mem = realloc( me->ignoredQTypes, newCount * sizeof( int ) );
8225 require_return_value( mem, kNoMemoryErr );
8226
8227 me->ignoredQTypes = mem;
8228 me->ignoredQTypes[ me->ignoredQTypeCount++ ] = inQType;
8229 return( kNoErr );
8230 }
8231
8232 //===========================================================================================================================
8233
8234 static void _DNSServerStartOnQueue( void *inContext );
8235 static void _DNSServerStartInternal( DNSServerRef inServer );
8236 static OSStatus _DNSServerSetUpSockets( DNSServerRef inServer );
8237 static void _DNSServerStopInternal( void *inContext, OSStatus inError );
8238 static SocketContext *
8239 _DNSServerSocketContextCreate(
8240 SocketRef inSock,
8241 DNSServerRef inServer,
8242 size_t inIndex,
8243 OSStatus * outError );
8244
8245 static void _DNSServerStart( DNSServerRef me )
8246 {
8247 CFRetain( me );
8248 dispatch_async_f( me->queue, me, _DNSServerStartOnQueue );
8249 }
8250
8251 static void _DNSServerStartOnQueue( void *inContext )
8252 {
8253 const DNSServerRef me = (DNSServerRef) inContext;
8254
8255 _DNSServerStartInternal( me );
8256 CFRelease( me );
8257 }
8258
8259 static void _DNSServerStartInternal( DNSServerRef me )
8260 {
8261 OSStatus err;
8262 struct timeval now;
8263 SocketRef sock = kInvalidSocketRef;
8264 SocketContext * sockCtx = NULL;
8265 int year, month, day;
8266
8267 require_action_quiet( !me->started && !me->stopped, exit, err = kNoErr );
8268 me->started = true;
8269 CFRetain( me );
8270
8271 err = _DNSServerSetUpSockets( me );
8272 require_noerr( err, exit );
8273
8274 if( me->startHandler ) me->startHandler( me->addrArray, me->addrCount, me->userContext );
8275
8276 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
8277 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
8278
8279 gettimeofday( &now, NULL );
8280 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
8281 NULL, NULL, NULL );
8282 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
8283 err = kNoErr;
8284
8285 exit:
8286 ForgetSocket( &sock );
8287 if( sockCtx ) SocketContextRelease( sockCtx );
8288 if( err ) _DNSServerStopInternal( me, err );
8289 }
8290
8291 typedef struct
8292 {
8293 SocketRef sockUDP;
8294 SocketRef sockTCP;
8295
8296 } _DNSServerSocketPair;
8297
8298 #define kDNSServerMaxBindTryCount 10
8299
8300 static OSStatus _DNSServerSetUpSockets( DNSServerRef me )
8301 {
8302 OSStatus err;
8303 SocketContext * sockCtx = NULL;
8304 _DNSServerSocketPair * sockPairs = NULL;
8305 _DNSServerSocketPair * sockPairsHeap = NULL;
8306 _DNSServerSocketPair sockPairsStack[ 16 ];
8307 size_t i;
8308 const size_t addrCount = me->addrCount; // Don't use me->addrCount to avoid false analyzer warning.
8309 int portWanted, tryCount, tryCountMax;
8310
8311 require_action_quiet( addrCount > 0, exit, err = kNoErr );
8312
8313 sockPairs = sockPairsStack;
8314 if( me->addrCount > countof( sockPairsStack ) )
8315 {
8316 sockPairsHeap = (_DNSServerSocketPair *) calloc( me->addrCount, sizeof( *sockPairsHeap ) );
8317 require_action( sockPairsHeap, exit, err = kNoMemoryErr );
8318 sockPairs = sockPairsHeap;
8319 }
8320 for( i = 0; i < addrCount; ++i )
8321 {
8322 sockPairs[ i ].sockUDP = kInvalidSocketRef;
8323 sockPairs[ i ].sockTCP = kInvalidSocketRef;
8324 }
8325 // Create server sockets.
8326
8327 err = kNoErr;
8328 portWanted = SockAddrGetPort( &me->addrArray[ 0 ] );
8329 tryCountMax = ( portWanted == 0 ) ? kDNSServerMaxBindTryCount : 1;
8330 for( tryCount = 0; tryCount < tryCountMax; ++tryCount )
8331 {
8332 int portDefault = 0;
8333
8334 for( i = 0; i < addrCount; ++i )
8335 {
8336 sockaddr_ip * const sip = &me->addrArray[ i ];
8337 _DNSServerSocketPair * const pair = &sockPairs[ i ];
8338 const void * address;
8339 SocketRef sock;
8340 int port, portActual;
8341 sockaddr_ip tmpSA;
8342
8343 switch( sip->sa.sa_family )
8344 {
8345 case AF_INET: address = &sip->v4.sin_addr.s_addr; break;
8346 case AF_INET6: address = sip->v6.sin6_addr.s6_addr; break;
8347 default:
8348 ds_ulog( kLogLevelError, "Unhandled address family %d", sip->sa.sa_family );
8349 err = kTypeErr;
8350 goto exit;
8351 }
8352 // Create UDP socket.
8353 // Initially, portWanted is the port requested by the user. If it's 0, then the user wants any available
8354 // ephemeral port. If it's negative, then the user would like a port number equal to its absolute value, but
8355 // will settle for any available ephemeral port, if it's not available. The actual port number that was used
8356 // will be stored in portActual and used for the remaining addresses that don't specify a non-zero port.
8357
8358 port = ( portWanted == 0 ) ? portDefault : portWanted;
8359 err = _ServerSocketOpenEx2( sip->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP, address, port, &portActual,
8360 kSocketBufferSize_DontSet, true, &sock );
8361 if( err == EADDRINUSE )
8362 {
8363 SockAddrCopy( sip, &tmpSA );
8364 SockAddrSetPort( &tmpSA, port );
8365 ds_ulog( kLogLevelError, "IP address %##a is already in use for UDP\n", &tmpSA );
8366 break;
8367 }
8368 require_noerr( err, exit );
8369 check( ( portWanted == 0 ) || ( portActual == portWanted ) );
8370
8371 ForgetSocket( &pair->sockUDP );
8372 pair->sockUDP = sock;
8373 sock = kInvalidSocketRef;
8374 if( portDefault == 0 ) portDefault = portActual;
8375
8376 // Create TCP socket.
8377
8378 err = _ServerSocketOpenEx2( sip->sa.sa_family, SOCK_STREAM, IPPROTO_TCP, address, portActual, NULL,
8379 kSocketBufferSize_DontSet, false, &sock );
8380 if( err == EADDRINUSE )
8381 {
8382 SockAddrCopy( sip, &tmpSA );
8383 SockAddrSetPort( &tmpSA, portActual );
8384 ds_ulog( kLogLevelError, "IP address %##a is already in use for TCP\n", &tmpSA );
8385 break;
8386 }
8387 require_noerr( err, exit );
8388
8389 ForgetSocket( &pair->sockTCP );
8390 pair->sockTCP = sock;
8391 sock = kInvalidSocketRef;
8392
8393 SockAddrSetPort( sip, portActual );
8394 }
8395 if( !err ) break;
8396 }
8397 require_noerr( err, exit );
8398
8399 // Create read sources for server sockets.
8400
8401 for( i = 0; i < addrCount; ++i )
8402 {
8403 const sockaddr_ip * const sip = &me->addrArray[ i ];
8404 dispatch_source_t * const readSourceUDPPtr = &me->readSourceArrayUDP[ i ];
8405 dispatch_source_t * const readSourceTCPPtr = &me->readSourceArrayTCP[ i ];
8406 _DNSServerSocketPair * const pair = &sockPairs[ i ];
8407
8408 // Create read source for UDP socket.
8409
8410 check( IsValidSocket( pair->sockUDP ) );
8411 sockCtx = _DNSServerSocketContextCreate( pair->sockUDP, me, i, &err );
8412 require_noerr( err, exit );
8413 pair->sockUDP = kInvalidSocketRef;
8414
8415 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler,
8416 sockCtx, readSourceUDPPtr );
8417 require_noerr( err, exit );
8418 dispatch_resume( *readSourceUDPPtr );
8419 sockCtx = NULL;
8420
8421 // Create read source for TCP socket.
8422
8423 check( IsValidSocket( pair->sockTCP ) );
8424 sockCtx = _DNSServerSocketContextCreate( pair->sockTCP, me, i, &err );
8425 require_noerr( err, exit );
8426 pair->sockTCP = kInvalidSocketRef;
8427
8428 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPAcceptHandler, SocketContextCancelHandler,
8429 sockCtx, readSourceTCPPtr );
8430 require_noerr( err, exit );
8431 dispatch_resume( *readSourceTCPPtr );
8432 sockCtx = NULL;
8433
8434 ds_ulog( kLogLevelInfo, "Server is listening on %##a\n", sip );
8435 }
8436
8437 exit:
8438 if( sockPairs )
8439 {
8440 for( i = 0; i < addrCount; ++i )
8441 {
8442 ForgetSocket( &sockPairs[ i ].sockUDP );
8443 ForgetSocket( &sockPairs[ i ].sockTCP );
8444 }
8445 }
8446 FreeNullSafe( sockPairsHeap );
8447 if( sockCtx ) SocketContextRelease( sockCtx );
8448 return( err );
8449 }
8450
8451 //===========================================================================================================================
8452
8453 typedef struct
8454 {
8455 DNSServerRef server;
8456 size_t index;
8457
8458 } DNSServerContext;
8459
8460 static void _DNSServerContextFree( DNSServerContext *inCtx );
8461 static void _DNSServerSocketContextFinalizer( void *inCtx );
8462
8463 static SocketContext *
8464 _DNSServerSocketContextCreate(
8465 SocketRef inSock,
8466 DNSServerRef inServer,
8467 size_t inIndex,
8468 OSStatus * outError )
8469 {
8470 OSStatus err;
8471 SocketContext * sockCtx = NULL;
8472 DNSServerContext * ctx;
8473
8474 ctx = (DNSServerContext *) calloc( 1, sizeof( *ctx ) );
8475 require_action( ctx, exit, err = kNoMemoryErr );
8476
8477 ctx->index = inIndex;
8478 ctx->server = inServer;
8479 CFRetain( ctx->server );
8480
8481 sockCtx = SocketContextCreateEx( inSock, ctx, _DNSServerSocketContextFinalizer, &err );
8482 require_noerr( err, exit );
8483 ctx = NULL;
8484
8485 exit:
8486 if( outError ) *outError = err;
8487 if( ctx ) _DNSServerContextFree( ctx );
8488 return( sockCtx );
8489 }
8490
8491 static void _DNSServerSocketContextFinalizer( void *inCtx )
8492 {
8493 _DNSServerContextFree( (DNSServerContext *) inCtx );
8494 }
8495
8496 static void _DNSServerContextFree( DNSServerContext *inCtx )
8497 {
8498 ForgetCF( &inCtx->server );
8499 free( inCtx );
8500 }
8501
8502 //===========================================================================================================================
8503
8504 static void _DNSServerStopOnQueue( void *inContext );
8505 static void _DNSServerStop2( void *inContext );
8506
8507 static void _DNSServerStop( DNSServerRef me )
8508 {
8509 CFRetain( me );
8510 dispatch_async_f( me->queue, me, _DNSServerStopOnQueue );
8511 }
8512
8513 static void _DNSServerStopOnQueue( void *inContext )
8514 {
8515 DNSServerRef const me = (DNSServerRef) inContext;
8516
8517 _DNSServerStopInternal( me, kNoErr );
8518 CFRelease( me );
8519 }
8520
8521 static void _DNSServerStopInternal( void *inContext, OSStatus inError )
8522 {
8523 DNSServerRef const me = (DNSServerRef) inContext;
8524 DNSServerConnectionRef cnx;
8525 size_t i;
8526
8527 require_quiet( !me->stopped, exit );
8528 me->stopped = true;
8529
8530 me->stopErr = inError;
8531 if( me->responseList )
8532 {
8533 _DNSServerDelayedResponseListFree( me->responseList );
8534 me->responseList = NULL;
8535 }
8536 dispatch_source_forget( &me->responseTimer );
8537 for( i = 0; i < me->addrCount; ++i )
8538 {
8539 dispatch_source_forget( &me->readSourceArrayUDP[ i ] );
8540 dispatch_source_forget( &me->readSourceArrayTCP[ i ] );
8541 }
8542 while( ( cnx = me->connectionList ) != NULL )
8543 {
8544 me->connectionList = cnx->next;
8545 _DNSServerConnectionStop( cnx, false );
8546 cnx->next = NULL;
8547 CFRelease( cnx );
8548 }
8549 dispatch_source_forget( &me->connectionTimer );
8550
8551 CFRetain( me );
8552 dispatch_async_f( me->queue, me, _DNSServerStop2 );
8553 if( me->started ) CFRelease( me );
8554
8555 exit:
8556 return;
8557 }
8558
8559 static void _DNSServerStop2( void *inContext )
8560 {
8561 DNSServerRef const me = (DNSServerRef) inContext;
8562
8563 if( me->stopHandler ) me->stopHandler( me->stopErr, me->userContext );
8564 CFRelease( me );
8565 }
8566
8567 //===========================================================================================================================
8568
8569 static void _DNSServerUDPReadHandler( void *inContext )
8570 {
8571 OSStatus err;
8572 SocketContext * const sockCtx = (SocketContext *) inContext;
8573 const DNSServerContext * const ctx = (DNSServerContext *) sockCtx->userContext;
8574 const DNSServerRef me = ctx->server;
8575 ssize_t n;
8576 sockaddr_ip client;
8577 socklen_t clientLen;
8578 uint8_t * respPtr = NULL; // malloc'd
8579 size_t respLen;
8580 uint8_t msg[ 512 ];
8581
8582 // Receive message.
8583
8584 clientLen = (socklen_t) sizeof( client );
8585 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &client.sa, &clientLen );
8586 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
8587 require_noerr( err, exit );
8588
8589 if( n < kDNSHeaderLength )
8590 {
8591 ds_ulog( kLogLevelInfo, "UDP: Received %zd bytes from %##a to %##a: Message is too small (< %d bytes)\n",
8592 n, &client, &me->addrArray[ ctx->index ], kDNSHeaderLength );
8593 goto exit;
8594 }
8595 ds_ulog( kLogLevelInfo, "UDP: Received %zd bytes from %##a to %##a -- %.1{du:dnsmsg}\n",
8596 n, &client, &me->addrArray[ ctx->index ], msg, (size_t) n );
8597
8598 // Create response.
8599
8600 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, ctx->index + 1, &respPtr, &respLen );
8601 if( err == kSkipErr ) ds_ulog( kLogLevelInfo, "UDP: Ignoring query\n" );
8602 require_noerr_quiet( err, exit );
8603
8604 if( me->responseDelayMs > 0 ) // Defer response.
8605 {
8606 err = _DNSServerScheduleDelayedResponse( me, sockCtx->sock, &client.sa, respPtr, respLen, ctx->index );
8607 require_noerr( err, exit );
8608 respPtr = NULL;
8609 }
8610 else // Send response.
8611 {
8612 ds_ulog( kLogLevelInfo, "UDP: Sending %zu byte response from %##a to %##a -- %.1{du:dnsmsg}\n",
8613 respLen, &me->addrArray[ ctx->index ], &client, respPtr, respLen );
8614
8615 n = sendto( sockCtx->sock, (char *) respPtr, respLen, 0, &client.sa, clientLen );
8616 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) respLen, n );
8617 require_noerr( err, exit );
8618 }
8619
8620 exit:
8621 FreeNullSafe( respPtr );
8622 }
8623
8624 //===========================================================================================================================
8625
8626 static void _DNSServerSendDelayedResponses( void *inContext );
8627
8628 static OSStatus
8629 _DNSServerScheduleDelayedResponse(
8630 DNSServerRef me,
8631 SocketRef inSock,
8632 const struct sockaddr * inDestAddr,
8633 uint8_t * inMsgPtr,
8634 size_t inMsgLen,
8635 size_t inIndex )
8636 {
8637 OSStatus err;
8638 DNSServerDelayedResponse * resp;
8639 DNSServerDelayedResponse * newResp;
8640 DNSServerDelayedResponse ** ptr;
8641 uint64_t dueTicks;
8642
8643 dueTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
8644 newResp = (DNSServerDelayedResponse *) calloc( 1, sizeof( *newResp ) );
8645 require_action( newResp, exit, err = kNoMemoryErr );
8646
8647 newResp->dueTicks = dueTicks;
8648 newResp->msgPtr = inMsgPtr;
8649 newResp->msgLen = inMsgLen;
8650 newResp->index = inIndex;
8651 newResp->sock = inSock;
8652 SockAddrCopy( inDestAddr, &newResp->client );
8653
8654 if( !me->responseList || ( _TicksDiff( dueTicks, me->responseList->dueTicks ) < 0 ) )
8655 {
8656 dispatch_source_forget( &me->responseTimer );
8657 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( me->responseDelayMs ), 0, me->queue,
8658 _DNSServerSendDelayedResponses, me, &me->responseTimer );
8659 require_noerr( err, exit );
8660 dispatch_resume( me->responseTimer );
8661 }
8662 for( ptr = &me->responseList; ( resp = *ptr ) != NULL; ptr = &resp->next )
8663 {
8664 if( _TicksDiff( newResp->dueTicks, resp->dueTicks ) < 0 ) break;
8665 }
8666 newResp->next = resp;
8667 *ptr = newResp;
8668 newResp = NULL;
8669 err = kNoErr;
8670
8671 exit:
8672 if( newResp ) _DNSServerDelayedResponseFree( newResp );
8673 return( err );
8674 }
8675
8676 static void _DNSServerSendDelayedResponses( void *inContext )
8677 {
8678 OSStatus err;
8679 const DNSServerRef me = (DNSServerRef) inContext;
8680 DNSServerDelayedResponse * resp;
8681 DNSServerDelayedResponse * freeList;
8682 int64_t deltaTicks;
8683
8684 dispatch_source_forget( &me->responseTimer );
8685
8686 deltaTicks = -1;
8687 freeList = NULL;
8688 while( ( resp = me->responseList ) != NULL )
8689 {
8690 ssize_t n;
8691 uint64_t nowTicks = UpTicks();
8692
8693 deltaTicks = _TicksDiff( resp->dueTicks, nowTicks );
8694 if( deltaTicks > 0 ) break;
8695 me->responseList = resp->next;
8696
8697 ds_ulog( kLogLevelInfo, "UDP: Sending %zu byte delayed response from %##a to %##a -- %.1{du:dnsmsg}\n",
8698 resp->msgLen, &me->addrArray[ resp->index ], &resp->client, resp->msgPtr, resp->msgLen );
8699
8700 n = sendto( resp->sock, (char *) resp->msgPtr, resp->msgLen, 0, &resp->client.sa, SockAddrGetSize( &resp->client ) );
8701 err = map_socket_value_errno( resp->sock, n == (ssize_t) resp->msgLen, n );
8702 check_noerr( err );
8703
8704 resp->next = freeList;
8705 freeList = resp;
8706 }
8707 if( deltaTicks > 0 )
8708 {
8709 uint64_t deltaNs;
8710
8711 deltaNs = UpTicksToNanoseconds( (uint64_t) deltaTicks );
8712 if( deltaNs > INT64_MAX ) deltaNs = INT64_MAX;
8713
8714 err = DispatchTimerOneShotCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) deltaNs ), 0, me->queue,
8715 _DNSServerSendDelayedResponses, me, &me->responseTimer );
8716 require_noerr( err, exit );
8717 dispatch_resume( me->responseTimer );
8718 }
8719
8720 exit:
8721 if( freeList ) _DNSServerDelayedResponseListFree( freeList );
8722 }
8723
8724 //===========================================================================================================================
8725
8726 static void _DNSServerDelayedResponseFree( DNSServerDelayedResponse *inResp )
8727 {
8728 ForgetMem( &inResp->msgPtr );
8729 inResp->sock = kInvalidSocketRef;
8730 free( inResp );
8731 }
8732
8733 //===========================================================================================================================
8734
8735 static void _DNSServerDelayedResponseListFree( DNSServerDelayedResponse *inList )
8736 {
8737 DNSServerDelayedResponse * resp;
8738
8739 while( ( resp = inList ) != NULL )
8740 {
8741 inList = resp->next;
8742 _DNSServerDelayedResponseFree( resp );
8743 }
8744 }
8745
8746 //===========================================================================================================================
8747
8748 #define kDNSServerConnectionExpirationTimeSecs 5
8749 #define kDNSServerConnectionExpirationTimeMs ( kDNSServerConnectionExpirationTimeSecs * kMillisecondsPerSecond )
8750
8751 static void _DNSServerTCPAcceptHandler( void *inContext )
8752 {
8753 OSStatus err;
8754 SocketContext * const sockCtx = (SocketContext *) inContext;
8755 const DNSServerContext * const ctx = (DNSServerContext *) sockCtx->userContext;
8756 const DNSServerRef me = ctx->server;
8757 DNSServerConnectionRef cnx = NULL;
8758 sockaddr_ip remote, local;
8759 socklen_t len;
8760 SocketRef sock;
8761
8762 len = (socklen_t) sizeof( remote );
8763 sock = accept( sockCtx->sock, &remote.sa, &len );
8764 err = map_socket_creation_errno( sock );
8765 require_noerr( err, exit );
8766
8767 len = (socklen_t) sizeof( local );
8768 err = getsockname( sock, &local.sa, &len );
8769 if( unlikely( err ) ) SockAddrCopy( &me->addrArray[ ctx->index ], &local );
8770
8771 err = _DNSServerConnectionCreate( me, &local.sa, &remote.sa, ctx->index, &cnx );
8772 require_noerr_quiet( err, exit );
8773
8774 err = _DNSServerConnectionStart( cnx, sock );
8775 require_noerr( err, exit );
8776 sock = kInvalidSocketRef;
8777
8778 if( !me->connectionList ) _DNSServerResetConnectionTimerMs( me, kDNSServerConnectionExpirationTimeMs );
8779 cnx->next = me->connectionList;
8780 me->connectionList = cnx;
8781 cnx = NULL;
8782
8783 exit:
8784 ForgetSocket( &sock );
8785 if( cnx )
8786 {
8787 _DNSServerConnectionStop( cnx, true );
8788 CFRelease( cnx );
8789 }
8790 }
8791
8792 //===========================================================================================================================
8793
8794 static void _DNSServerConnectionTimerHandler( void *inContext )
8795 {
8796 const DNSServerRef me = (DNSServerRef) inContext;
8797 DNSServerConnectionRef cnx;
8798 DNSServerConnectionRef * ptr;
8799 uint64_t nowTicks;
8800 int64_t delta, deltaMin;
8801
8802 nowTicks = UpTicks();
8803 deltaMin = INT64_MAX;
8804 ptr = &me->connectionList;
8805 while( ( cnx = *ptr ) != NULL )
8806 {
8807 delta = _TicksDiff( cnx->expirationTicks, nowTicks );
8808 if( delta <= 0 )
8809 {
8810 ds_ulog( kLogLevelInfo, "Timing out TCP connection: %##a <-> %##a\n", &cnx->local, &cnx->remote );
8811 *ptr = cnx->next;
8812 cnx->next = NULL;
8813 _DNSServerConnectionStop( cnx, false );
8814 CFRelease( cnx );
8815 }
8816 else
8817 {
8818 if( delta < deltaMin ) deltaMin = delta;
8819 ptr = &cnx->next;
8820 }
8821 }
8822 if( me->connectionList )
8823 {
8824 const uint64_t timeMs = UpTicksToMilliseconds( (uint64_t) deltaMin );
8825
8826 check( timeMs <= kDNSServerConnectionExpirationTimeMs );
8827 _DNSServerResetConnectionTimerMs( me, timeMs + 1 );
8828 }
8829 }
8830
8831 //===========================================================================================================================
8832
8833 static void _DNSServerResetConnectionTimerMs( DNSServerRef me, uint64_t inTimeoutMs )
8834 {
8835 OSStatus err;
8836
8837 dispatch_source_forget( &me->connectionTimer );
8838 if( inTimeoutMs == 0 ) inTimeoutMs = 1;
8839 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( inTimeoutMs ),
8840 UINT64_C( 10 ) * kNanosecondsPerMillisecond, me->queue, _DNSServerConnectionTimerHandler, me, &me->connectionTimer );
8841 if( likely( !err ) ) dispatch_resume( me->connectionTimer );
8842 else ds_ulog( kLogLevelError, "Failed to create connection timer: %#m\n", err );
8843 }
8844
8845 //===========================================================================================================================
8846
8847 static OSStatus
8848 _DNSServerInitializeResponseMessage(
8849 DataBuffer * inDB,
8850 uint16_t inID,
8851 uint16_t inFlags,
8852 const uint8_t * inQName,
8853 uint16_t inQType,
8854 uint16_t inQClass );
8855 static OSStatus
8856 _DNSServerAnswerQueryDynamically(
8857 DNSServerRef inServer,
8858 const uint8_t * inQName,
8859 int inQType,
8860 int inQClass,
8861 size_t inIndex,
8862 Boolean inForTCP,
8863 Boolean inDNSSEC,
8864 DataBuffer * inDB );
8865
8866 static OSStatus
8867 _DNSServerAnswerQuery(
8868 DNSServerRef me,
8869 const uint8_t * const inMsgPtr,
8870 const size_t inMsgLen,
8871 const size_t inIndex,
8872 const Boolean inForTCP,
8873 uint8_t ** const outResponsePtr,
8874 size_t * const outResponseLen )
8875 {
8876 OSStatus err;
8877 DataBuffer db;
8878 const uint8_t * ptr;
8879 const DNSHeader * hdr;
8880 const uint8_t * optPtr;
8881 size_t optLen;
8882 unsigned int qflags, rcode;
8883 uint16_t msgID, qtype, qclass, rflags;
8884 uint8_t qname[ kDomainNameLengthMax ];
8885 uint8_t dbBuf[ 512 ];
8886 Boolean dnssecOK;
8887
8888 DataBuffer_Init( &db, dbBuf, sizeof( dbBuf ), kDNSMaxTCPMessageSize );
8889
8890 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
8891
8892 hdr = (const DNSHeader *) inMsgPtr;
8893 qflags = DNSHeaderGetFlags( hdr );
8894
8895 // Minimal checking of the query message's header.
8896
8897 require_action_quiet( !( qflags & kDNSHeaderFlag_Response ), exit, err = kRequestErr );
8898 require_action_quiet( DNSFlagsGetOpCode( qflags ) == kDNSOpCode_Query, exit, err = kRequestErr );
8899 require_action_quiet( DNSHeaderGetQuestionCount( hdr ) == 1, exit, err = kRequestErr );
8900
8901 ptr = (const uint8_t *) &hdr[ 1 ];
8902 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
8903 require_noerr( err, exit );
8904
8905 // Check if this query should be ignored because of its QTYPE.
8906
8907 if( qclass == kDNSClassType_IN )
8908 {
8909 size_t i;
8910
8911 for( i = 0; i < me->ignoredQTypeCount; ++i )
8912 {
8913 if( qtype == me->ignoredQTypes[ i ] )
8914 {
8915 err = kSkipErr;
8916 goto exit;
8917 }
8918 }
8919 }
8920 // Set up response flags.
8921
8922 rflags = kDNSHeaderFlag_Response;
8923 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
8924 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
8925
8926 // Get OPT record, if any.
8927
8928 err = DNSMessageGetOptRecord( inMsgPtr, inMsgLen, &optPtr, &optLen );
8929 require_noerr_action_quiet( err, done, rcode = kDNSRCode_FormErr );
8930
8931 // Create a tentative response message.
8932
8933 msgID = DNSHeaderGetID( hdr );
8934 if( me->badUDPMode && !inForTCP ) ++msgID;
8935 err = _DNSServerInitializeResponseMessage( &db, msgID, rflags, qname, qtype, qclass );
8936 require_noerr_action( err, done, rcode = kDNSRCode_ServFail );
8937
8938 // Complete the response message.
8939
8940 dnssecOK = false;
8941 if( optPtr )
8942 {
8943 const dns_fixed_fields_opt * opt = (const dns_fixed_fields_opt *) optPtr;
8944
8945 if( dns_fixed_fields_opt_get_extended_flags( opt ) & kDNSExtendedFlag_DNSSECOK ) dnssecOK = true;
8946 }
8947 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inIndex, inForTCP, dnssecOK, &db );
8948 if( err == kSkipErr ) goto exit;
8949 rcode = err ? kDNSRCode_ServFail : 0;
8950
8951 // Create an error response if there was a format error or a server failure.
8952
8953 done:
8954 if( rcode != 0 )
8955 {
8956 DNSFlagsSetRCode( rflags, rcode );
8957 err = _DNSServerInitializeResponseMessage( &db, DNSHeaderGetID( hdr ), rflags, qname, qtype, qclass );
8958 require_noerr( err, exit );
8959 }
8960 err = DataBuffer_Detach( &db, outResponsePtr, outResponseLen );
8961 require_noerr( err, exit );
8962
8963 exit:
8964 DataBuffer_Free( &db );
8965 return( err );
8966 }
8967
8968 //===========================================================================================================================
8969
8970 static OSStatus
8971 _DNSServerInitializeResponseMessage(
8972 DataBuffer * inDB,
8973 uint16_t inID,
8974 uint16_t inFlags,
8975 const uint8_t * inQName,
8976 uint16_t inQType,
8977 uint16_t inQClass )
8978 {
8979 OSStatus err;
8980 DNSHeader header;
8981
8982 DataBuffer_Reset( inDB );
8983
8984 memset( &header, 0, sizeof( header ) );
8985 DNSHeaderSetID( &header, inID );
8986 DNSHeaderSetFlags( &header, inFlags );
8987 DNSHeaderSetQuestionCount( &header, 1 );
8988
8989 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
8990 require_noerr( err, exit );
8991
8992 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), inQType, inQClass );
8993 require_noerr( err, exit );
8994
8995 exit:
8996 return( err );
8997 }
8998
8999 //===========================================================================================================================
9000
9001 // DNS Server QNAME Labels
9002
9003 #define kLabel_IPv4 "ipv4"
9004 #define kLabel_IPv6 "ipv6"
9005 #define kLabelPrefix_Alias "alias"
9006 #define kLabelPrefix_AliasTTL "alias-ttl"
9007 #define kLabelPrefix_Count "count-"
9008 #define kLabelPrefix_Index "index-"
9009 #define kLabelPrefix_RCode "rcode-"
9010 #define kLabelPrefix_SRV "srv-"
9011 #define kLabelPrefix_Tag "tag-"
9012 #define kLabelPrefix_TTL "ttl-"
9013
9014 // Experimental Labels
9015
9016 #define kLabelPrefix_PDelay "pdelay-" // Specifies an additional simulated processing delay in milliseconds.
9017 #define kLabelPrefix_Zone "z-" // format: z-<algorithm mnemonic exclude '-'>-<zone index>
9018
9019 typedef struct
9020 {
9021 uint16_t priority; // Priority from SRV label.
9022 uint16_t weight; // Weight from SRV label.
9023 uint16_t port; // Port number from SRV label.
9024 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
9025 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
9026
9027 } ParsedSRV;
9028
9029 typedef uint32_t DNSNameFlags;
9030 #define kDNSNameFlag_HasA ( 1U << 0 )
9031 #define kDNSNameFlag_HasAAAA ( 1U << 1 )
9032 #define kDNSNameFlag_HasSOA ( 1U << 2 )
9033 #define kDNSNameFlag_HasSRV ( 1U << 3 )
9034 #define kDNSNameFlag_HasPTRv4 ( 1U << 4 )
9035 #define kDNSNameFlag_HasPTRv6 ( 1U << 5 )
9036 #define kDNSNameFlag_HasRRSIG ( 1U << 6 )
9037 #define kDNSNameFlag_HasDNSKEY ( 1U << 7 )
9038 #define kDNSNameFlag_HasDS ( 1U << 8 )
9039
9040 #define kAliasTTLCountMax ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
9041 #define kParsedSRVCountMax ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
9042
9043 static Boolean
9044 _DNSServerParseHostName(
9045 DNSServerRef inServer,
9046 const uint8_t * inQName,
9047 uint32_t * outAliasCount,
9048 uint32_t outAliasTTLs[ kAliasTTLCountMax ],
9049 uint32_t * outAliasTTLCount,
9050 uint32_t * outCount,
9051 uint32_t * outRandCount,
9052 uint32_t * outIndex,
9053 int * outRCode,
9054 uint32_t * outTTL,
9055 uint32_t * outProcDelayMs,
9056 DNSNameFlags * outFlags,
9057 const uint8_t ** outZone,
9058 const uint8_t ** outZoneParent,
9059 DNSKeyInfoRef * outZSK,
9060 DNSKeyInfoRef * outKSK,
9061 DNSKeyInfoRef * outParentZSK );
9062 static Boolean
9063 _DNSServerParseSRVName(
9064 DNSServerRef inServer,
9065 const uint8_t * inName,
9066 const uint8_t ** outDomainPtr,
9067 size_t * outDomainLen,
9068 ParsedSRV outSRVArray[ kParsedSRVCountMax ],
9069 size_t * outSRVCount );
9070 static Boolean _DNSServerParseReverseIPv4Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID );
9071 static Boolean _DNSServerParseReverseIPv6Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID );
9072 #if( DEBUG )
9073 static void
9074 _DNSServerSigCheck(
9075 const uint8_t * inOwner,
9076 int inTypeCovered,
9077 const void * inMsgPtr,
9078 size_t inMsgLen,
9079 const uint8_t * inSignaturePtr,
9080 const size_t inSignatureLen,
9081 DNSKeyInfoRef inKeyInfo );
9082 #endif
9083
9084 typedef enum
9085 {
9086 kQueryStatus_Null = 0,
9087 kQueryStatus_OK = 1,
9088 kQueryStatus_Truncated = 2,
9089 kQueryStatus_NotImplemented = 3,
9090 kQueryStatus_Refused = 4
9091
9092 } QueryStatus;
9093
9094 static OSStatus
9095 _DNSServerAnswerQueryDynamically(
9096 DNSServerRef me,
9097 const uint8_t * const inQName,
9098 const int inQType,
9099 const int inQClass,
9100 const size_t inIndex,
9101 const Boolean inForTCP,
9102 const Boolean inDNSSEC,
9103 DataBuffer * const inDB )
9104 {
9105 OSStatus err;
9106 uint32_t aliasCount = 0;
9107 uint32_t aliasTTLs[ kAliasTTLCountMax ];
9108 uint32_t aliasTTLCount = 0;
9109 uint32_t addrCount = 0;
9110 uint32_t randCount = 0;
9111 uint32_t index = 0;
9112 int rcodeOverride = -1;
9113 uint32_t ttl = 0;
9114 uint32_t procDelayMs = 0;
9115 DNSNameFlags nameFlags = 0;
9116 const uint8_t * zone = NULL;
9117 const uint8_t * zoneParent = NULL;
9118 DNSKeyInfoRef zsk = NULL;
9119 DNSKeyInfoRef ksk = NULL;
9120 DNSKeyInfoRef zskParent = NULL;
9121 const uint8_t * srvDomainPtr = NULL;
9122 size_t srvDomainLen = 0;
9123 ParsedSRV srvArray[ kParsedSRVCountMax ];
9124 size_t srvCount = 0;
9125 unsigned int hostID = 0;
9126 struct timeval now;
9127 DNSHeader * hdr;
9128 unsigned int flags;
9129 int rcode;
9130 QueryStatus status;
9131 unsigned int answerCount = 0;
9132 unsigned int additionalCount = 0;
9133 Boolean nameExists = false;
9134 uint8_t * qnameLower = NULL;
9135 size_t qnameLowerLen;
9136 const uint8_t * ownerLower;
9137 size_t ownerLowerLen;
9138 DataBuffer * sigMsg = NULL;
9139 DataBuffer sigDB;
9140 uint8_t sigBuf[ 256 ];
9141 uint8_t nameCPtr[ 2 ];
9142 uint64_t startTicks;
9143
9144 startTicks = UpTicks();
9145 require_action_quiet( inQClass == kDNSServiceClass_IN, done, status = kQueryStatus_NotImplemented );
9146
9147 nameExists = _DNSServerParseHostName( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount,
9148 &index, &rcodeOverride, &ttl, &procDelayMs, &nameFlags, &zone, &zoneParent, &zsk, &ksk, &zskParent );
9149 if( nameExists )
9150 {
9151 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
9152 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
9153 check( rcodeOverride <= 15 );
9154 check( !( nameFlags & kDNSNameFlag_HasRRSIG ) || ( zsk && ksk && zskParent ) );
9155
9156 if( aliasTTLCount > 0 ) aliasCount = (uint32_t) aliasTTLCount;
9157 if( index != 0 )
9158 {
9159 if( index == inIndex )
9160 {
9161 rcodeOverride = -1;
9162 }
9163 else
9164 {
9165 if ( rcodeOverride < 0 )
9166 {
9167 err = kSkipErr;
9168 goto exit;
9169 }
9170 else
9171 {
9172 addrCount = 0;
9173 }
9174 }
9175 }
9176 }
9177 else if( ( nameExists = _DNSServerParseSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) ) )
9178 {
9179 nameFlags = kDNSNameFlag_HasSRV;
9180 }
9181 else if( ( nameExists = _DNSServerParseReverseIPv4Name( me, inQName, &hostID ) ) )
9182 {
9183 check( ( hostID >= 1 ) && ( hostID <= 255 ) );
9184
9185 nameFlags = kDNSNameFlag_HasPTRv4;
9186 }
9187 else if( ( nameExists = _DNSServerParseReverseIPv6Name( me, inQName, &hostID ) ) )
9188 {
9189 check( ( hostID >= 1 ) && ( hostID <= 255 ) );
9190
9191 nameFlags = kDNSNameFlag_HasPTRv6;
9192 }
9193 require_action_quiet( nameExists, done, status = kQueryStatus_OK );
9194
9195 err = DomainNameDupLower( inQName, &qnameLower, &qnameLowerLen );
9196 require_noerr( err, exit );
9197
9198 gettimeofday( &now, NULL );
9199 if( aliasCount > 0 )
9200 {
9201 size_t nameOffset, rdataLabelLen;
9202 const uint8_t * parentLower;
9203 size_t parentLowerLen = 0;
9204 uint32_t i;
9205 dns_fixed_fields_record recFields;
9206 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax ];
9207 uint8_t parentCPtr[ 2 ];
9208 Boolean needSig;
9209
9210 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". parentCPtr is a
9211 // name compression pointer to the second label of QNAME, i.e., the parent domain name of QNAME. It's used for
9212 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to
9213 // construct CNAME record names when the offset to the previous CNAME's RDATA doesn't fit in a compression
9214 // pointer.
9215
9216 DNSMessageWriteLabelPointer( parentCPtr, kDNSHeaderLength + ( 1 + inQName[ 0 ] ) );
9217
9218 rdataLabel[ 0 ] = 0;
9219 rdataLabelLen = 1;
9220 nameOffset = kDNSHeaderLength; // The name of the first CNAME record is equal to QNAME.
9221
9222 needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9223 if( needSig )
9224 {
9225 parentLower = DomainNameGetNextLabel( qnameLower );
9226 parentLowerLen = DomainNameLength( parentLower );
9227 }
9228 for( i = aliasCount; i >= 1; --i )
9229 {
9230 size_t nameLabelLen, nameLen, rdataLen, recordLen;
9231 uint32_t aliasTTL;
9232 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
9233 Boolean useNamePtr;
9234 const Boolean useAliasTTLs = ( aliasTTLCount > 0 ) ? true : false;
9235
9236 memcpy( nameLabel, rdataLabel, rdataLabelLen );
9237 nameLabelLen = rdataLabelLen;
9238 if( nameOffset <= kDNSCompressionOffsetMax )
9239 {
9240 DNSMessageWriteLabelPointer( nameCPtr, nameOffset );
9241 nameLen = sizeof( nameCPtr );
9242 useNamePtr = true;
9243 }
9244 else
9245 {
9246 nameLen = nameLabelLen + sizeof( parentCPtr );
9247 useNamePtr = false;
9248 }
9249
9250 if( i > 1 )
9251 {
9252 // There's at least one alias/CNAME left.
9253
9254 uint8_t * dst = &rdataLabel[ 1 ];
9255 const uint8_t * lim = &rdataLabel[ countof( rdataLabel ) ];
9256 size_t maxLen;
9257 int n;
9258
9259 maxLen = (size_t)( lim - dst );
9260 if( useAliasTTLs )
9261 {
9262 uint32_t j;
9263
9264 n = MemPrintF( dst, maxLen, "%s", kLabelPrefix_AliasTTL );
9265 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print AliasTTL label" );
9266 dst += n;
9267
9268 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
9269 {
9270 maxLen = (size_t)( lim - dst );
9271 n = MemPrintF( dst, maxLen, "-%u", aliasTTLs[ j ] );
9272 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print AliasTTL label" );
9273 dst += n;
9274 }
9275 }
9276 else if( i == 2 )
9277 {
9278 n = MemPrintF( dst, maxLen, "%s", kLabelPrefix_Alias );
9279 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print Alias label" );
9280 dst += n;
9281 }
9282 else
9283 {
9284 n = MemPrintF( dst, maxLen, "%s-%u", kLabelPrefix_Alias, i - 1 );
9285 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print Alias label" );
9286 dst += n;
9287 }
9288 rdataLabel[ 0 ] = (uint8_t)( dst - &rdataLabel[ 1 ] );
9289 rdataLabelLen = 1 + rdataLabel[ 0 ];
9290 rdataLen = rdataLabelLen + sizeof( parentCPtr );
9291 }
9292 else
9293 {
9294 // This is the final CNAME.
9295
9296 rdataLen = sizeof( parentCPtr );
9297 }
9298
9299 // If the transport is UDP, make sure the message is within the UDP size limit.
9300
9301 if( !inForTCP )
9302 {
9303 recordLen = nameLen + sizeof( recFields ) + rdataLen;
9304 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
9305 {
9306 status = kQueryStatus_Truncated;
9307 goto done;
9308 }
9309 }
9310 // Append CNAME record's NAME to response.
9311
9312 if( useNamePtr )
9313 {
9314 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9315 require_noerr( err, exit );
9316 }
9317 else
9318 {
9319 err = DataBuffer_Append( inDB, nameLabel, nameLabelLen );
9320 require_noerr( err, exit );
9321
9322 err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9323 require_noerr( err, exit );
9324 }
9325 // Append CNAME record's TYPE, CLASS, TTL, and RDLENGTH to response.
9326
9327 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
9328 dns_fixed_fields_record_init( &recFields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
9329 (uint16_t) rdataLen );
9330 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9331 require_noerr( err, exit );
9332
9333 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
9334
9335 nameOffset = DataBuffer_GetLen( inDB );
9336
9337 // Append CNAME record's RDATA to response.
9338
9339 if( i > 1 )
9340 {
9341 // There's at least one CNAME left.
9342
9343 err = DataBuffer_Append( inDB, rdataLabel, rdataLabelLen );
9344 require_noerr( err, exit );
9345 }
9346 err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9347 require_noerr( err, exit );
9348
9349 ++answerCount;
9350
9351 if( needSig )
9352 {
9353 dns_fixed_fields_rrsig sigFields;
9354 const size_t signerLen = DomainNameLength( zone );
9355 uint32_t inceptionSecs;
9356 uint8_t signature[ kDNSServerSignatureLengthMax ];
9357 size_t signatureLen;
9358 int labelCount;
9359 Boolean didSign;
9360
9361 // Initialize signing buffer.
9362
9363 check( !sigMsg );
9364 sigMsg = &sigDB;
9365 DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9366
9367 // Append RRSIG record RDATA fixed fields to signing buffer.
9368
9369 memset( &sigFields, 0, sizeof( sigFields ) );
9370 dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_CNAME );
9371 dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zsk ) );
9372 labelCount = DomainNameLabelCount( inQName );
9373 check( labelCount >= 0 );
9374 dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9375 dns_fixed_fields_rrsig_set_original_ttl( &sigFields, aliasTTL );
9376 inceptionSecs = (uint32_t) now.tv_sec;
9377 dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9378 dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9379 dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zsk ) );
9380
9381 err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9382 require_noerr( err, exit );
9383
9384 // Append RRSIG record RDATA signer to signing buffer.
9385
9386 err = DataBuffer_Append( sigMsg, zone, signerLen );
9387 require_noerr( err, exit );
9388
9389 // Append expanded CNAME record owner to signing buffer.
9390
9391 if( i == aliasCount )
9392 {
9393 err = DataBuffer_Append( sigMsg, qnameLower, qnameLowerLen );
9394 require_noerr( err, exit );
9395 }
9396 else
9397 {
9398 err = DataBuffer_Append( sigMsg, nameLabel, nameLabelLen );
9399 require_noerr( err, exit );
9400
9401 err = DataBuffer_Append( sigMsg, parentLower, parentLowerLen );
9402 require_noerr( err, exit );
9403 }
9404 // Append CNAME record fixed fields to signing buffer.
9405
9406 err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9407 require_noerr( err, exit );
9408
9409 // Append expanded CNAME record RDATA to signing buffer.
9410
9411 if( i > 1 )
9412 {
9413 // There's at least one CNAME left.
9414
9415 err = DataBuffer_Append( sigMsg, rdataLabel, rdataLabelLen );
9416 require_noerr( err, exit );
9417 }
9418 err = DataBuffer_Append( sigMsg, parentLower, parentLowerLen );
9419 require_noerr( err, exit );
9420
9421 // Compute signature with ZSK.
9422
9423 memset( signature, 0, sizeof( signature ) );
9424 didSign = DNSKeyInfoSign( zsk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
9425 signature, &signatureLen );
9426 require_quiet( didSign, exit );
9427
9428 #if( DEBUG )
9429 {
9430 const uint8_t * tmpPtr;
9431 uint8_t tmpBuf[ kDomainNameLengthMax ];
9432
9433 if( i == aliasCount )
9434 {
9435 tmpPtr = inQName;
9436 }
9437 else
9438 {
9439 memcpy( tmpBuf, nameLabel, nameLabelLen );
9440 memcpy( &tmpBuf[ nameLabelLen ], parentLower, parentLowerLen );
9441 tmpPtr = tmpBuf;
9442 }
9443 _DNSServerSigCheck( tmpPtr, kDNSServiceType_CNAME, DataBuffer_GetPtr( sigMsg ),
9444 DataBuffer_GetLen( sigMsg ), signature, signatureLen, zsk );
9445 }
9446 #endif
9447 // If the transport is UDP, make sure the message is within the UDP size limit.
9448
9449 rdataLen = sizeof( sigFields ) + signerLen + signatureLen;
9450 recordLen = nameLen + sizeof( recFields ) + rdataLen;
9451 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9452 {
9453 status = kQueryStatus_Truncated;
9454 goto done;
9455 }
9456 // Append RRSIG record NAME to response.
9457
9458 if( useNamePtr )
9459 {
9460 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9461 require_noerr( err, exit );
9462 }
9463 else
9464 {
9465 err = DataBuffer_Append( inDB, nameLabel, nameLabelLen );
9466 require_noerr( err, exit );
9467
9468 err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9469 require_noerr( err, exit );
9470 }
9471 // Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
9472
9473 dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, aliasTTL,
9474 (uint16_t) rdataLen );
9475 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9476 require_noerr( err, exit );
9477
9478 // Append RRSIG record RDATA fixed fields and signer to response.
9479
9480 err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
9481 require_noerr( err, exit );
9482
9483 err = DataBuffer_Append( inDB, zone, signerLen );
9484 require_noerr( err, exit );
9485
9486 // Append RRSIG record RDATA signature to response.
9487
9488 err = DataBuffer_Append( inDB, signature, signatureLen );
9489 require_noerr( err, exit );
9490
9491 ++answerCount;
9492 DataBuffer_Free( sigMsg );
9493 sigMsg = NULL;
9494 }
9495 }
9496 check_compile_time_code( sizeof( nameCPtr ) == sizeof( parentCPtr ) );
9497 memcpy( nameCPtr, parentCPtr, sizeof( nameCPtr ) );
9498
9499 ownerLower = DomainNameGetNextLabel( qnameLower );
9500 ownerLowerLen = DomainNameLength( ownerLower );
9501 }
9502 else
9503 {
9504 // There are no aliases, so initialize the name compression pointer to point to QNAME.
9505
9506 DNSMessageWriteLabelPointer( nameCPtr, kDNSHeaderLength );
9507
9508 ownerLower = qnameLower;
9509 ownerLowerLen = qnameLowerLen;
9510 }
9511
9512 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
9513 {
9514 dns_fixed_fields_record recFields;
9515 dns_fixed_fields_rrsig sigFields;
9516 uint8_t * idPtr; // Pointer to the host identifier portion of an IP address.
9517 size_t recordLen; // Length of the entire record.
9518 size_t rdataLen; // Length of record's RDATA.
9519 size_t signerLen = 0;
9520 unsigned int i; // For-loop counter.
9521 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
9522 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
9523 Boolean needSig;
9524
9525 if( inQType == kDNSServiceType_A )
9526 {
9527 uint32_t baseAddr;
9528
9529 require_action_quiet( nameFlags & kDNSNameFlag_HasA, done, status = kQueryStatus_OK );
9530
9531 rdataLen = 4;
9532 baseAddr = ( me->badUDPMode && !inForTCP ) ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
9533 WriteBig32( rdata, baseAddr );
9534 idPtr = &rdata[ 3 ]; // The last octet is the host identifier since the IPv4 address block is /24.
9535 }
9536 else
9537 {
9538 const uint8_t ( *baseAddr )[ 16 ];
9539
9540 require_action_quiet( nameFlags & kDNSNameFlag_HasAAAA, done, status = kQueryStatus_OK );
9541
9542 rdataLen = 16;
9543 baseAddr = ( me->badUDPMode && !inForTCP ) ? &kDNSServerBadBaseAddrV6 : &kDNSServerBaseAddrV6;
9544 memcpy( rdata, baseAddr, rdataLen );
9545 idPtr = &rdata[ 14 ]; // The last two octets are the host identifier since we allow up to 511 IPv6 addresses.
9546 }
9547
9548 if( randCount > 0 )
9549 {
9550 // Populate the array with all integers between 1 and <randCount>, inclusive.
9551
9552 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
9553
9554 // Prevent dubious static analyzer warning.
9555 // Note: _DNSServerParseHostName() already enforces randCount >= addrCount. Also, this require_fatal() check
9556 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
9557 // for some reason.
9558
9559 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
9560 addrCount, randCount );
9561
9562 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
9563 // 1 and <randCount>, inclusive.
9564 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
9565 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
9566
9567 for( i = 0; i < addrCount; ++i )
9568 {
9569 uint8_t tmp;
9570 uint32_t j;
9571
9572 j = RandomRange( i, randCount - 1 );
9573 if( i != j )
9574 {
9575 tmp = randIntegers[ i ];
9576 randIntegers[ i ] = randIntegers[ j ];
9577 randIntegers[ j ] = tmp;
9578 }
9579 }
9580 }
9581 needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9582 if( needSig )
9583 {
9584 uint32_t inceptionSecs;
9585 int labelCount;
9586
9587 // Initialize signing buffer.
9588
9589 check( !sigMsg );
9590 sigMsg = &sigDB;
9591 DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9592
9593 // Append RRSIG record RDATA fixed fields to signing buffer.
9594
9595 memset( &sigFields, 0, sizeof( sigFields ) );
9596 dns_fixed_fields_rrsig_set_type_covered( &sigFields, (uint16_t) inQType );
9597 dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zsk ) );
9598 labelCount = DomainNameLabelCount( ownerLower );
9599 check( labelCount >= 0 );
9600 dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9601 dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
9602 inceptionSecs = (uint32_t) now.tv_sec;
9603 dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9604 dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9605 dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zsk ) );
9606
9607 err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9608 require_noerr( err, exit );
9609
9610 // Append RRSIG record RDATA signer to signing buffer.
9611
9612 signerLen = DomainNameLength( zone );
9613 err = DataBuffer_Append( sigMsg, zone, signerLen );
9614 require_noerr( err, exit );
9615 }
9616 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9617 dns_fixed_fields_record_init( &recFields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
9618 for( i = 0; i < addrCount; ++i )
9619 {
9620 // If the transport is UDP, make sure the message is within the UDP size limit.
9621
9622 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9623 {
9624 status = kQueryStatus_Truncated;
9625 goto done;
9626 }
9627 // Append A/AAAA record NAME to response.
9628
9629 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9630 require_noerr( err, exit );
9631
9632 // Append A/AAAA record TYPE, CLASS, TTL, and RDLENGTH to response.
9633
9634 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9635 require_noerr( err, exit );
9636
9637 // Append A/AAAA record RDATA to response.
9638
9639 hostID = ( randCount > 0 ) ? randIntegers[ i ] : ( i + 1 );
9640 if( inQType == kDNSServiceType_A )
9641 {
9642 *idPtr = (uint8_t) hostID;
9643 }
9644 else
9645 {
9646 WriteBig16( idPtr, hostID );
9647 }
9648 err = DataBuffer_Append( inDB, rdata, rdataLen );
9649 require_noerr( err, exit );
9650
9651 ++answerCount;
9652 if( needSig )
9653 {
9654 // Append A/AAAA record to signing buffer.
9655
9656 err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
9657 require_noerr( err, exit );
9658
9659 err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9660 require_noerr( err, exit );
9661
9662 err = DataBuffer_Append( sigMsg, rdata, rdataLen );
9663 require_noerr( err, exit );
9664 }
9665 }
9666 if( needSig )
9667 {
9668 uint8_t signature[ kDNSServerSignatureLengthMax ];
9669 size_t signatureLen;
9670 Boolean didSign;
9671
9672 // Compute signature with ZSK.
9673
9674 memset( signature, 0, sizeof( signature ) );
9675 didSign = DNSKeyInfoSign( zsk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
9676 signature, &signatureLen );
9677 require_quiet( didSign, exit );
9678
9679 #if( DEBUG )
9680 _DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
9681 signatureLen, zsk );
9682 #endif
9683 // If the transport is UDP, make sure the message is within the UDP size limit.
9684
9685 rdataLen = sizeof( sigFields ) + signerLen + signatureLen;
9686 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9687 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9688 {
9689 status = kQueryStatus_Truncated;
9690 goto done;
9691 }
9692 // Append RRSIG record NAME to response.
9693
9694 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9695 require_noerr( err, exit );
9696
9697 // Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
9698
9699 dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
9700 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9701 require_noerr( err, exit );
9702
9703 // Append RRSIG record RDATA fixed fields and signer to response.
9704
9705 err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
9706 require_noerr( err, exit );
9707
9708 err = DataBuffer_Append( inDB, zone, signerLen );
9709 require_noerr( err, exit );
9710
9711 // Append RRSIG record RDATA signature to response.
9712
9713 err = DataBuffer_Append( inDB, signature, signatureLen );
9714 require_noerr( err, exit );
9715
9716 ++answerCount;
9717 DataBuffer_Free( sigMsg );
9718 sigMsg = NULL;
9719 }
9720 }
9721 else if( inQType == kDNSServiceType_SRV )
9722 {
9723 dns_fixed_fields_record recFields;
9724 size_t i;
9725
9726 require_action_quiet( nameFlags & kDNSNameFlag_HasSRV, done, status = kQueryStatus_OK );
9727
9728 dns_fixed_fields_record_init( &recFields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
9729 for( i = 0; i < srvCount; ++i )
9730 {
9731 dns_fixed_fields_srv srvFields;
9732 size_t rdataLen;
9733 size_t recordLen;
9734 const ParsedSRV * const srv = &srvArray[ i ];
9735
9736 // If the transport is UDP, make sure the message is within the UDP size limit.
9737
9738 rdataLen = sizeof( srvFields ) + srvDomainLen + srv->targetLen + 1;
9739 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9740 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9741 {
9742 status = kQueryStatus_Truncated;
9743 goto done;
9744 }
9745 // Append record NAME to response.
9746
9747 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9748 require_noerr( err, exit );
9749
9750 // Append record TYPE, CLASS, TTL, and RDLENGTH to response.
9751
9752 dns_fixed_fields_record_set_rdlength( &recFields, (uint16_t) rdataLen );
9753 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9754 require_noerr( err, exit );
9755
9756 // Append SRV RDATA priority, weight, and port to response.
9757
9758 dns_fixed_fields_srv_init( &srvFields, srv->priority, srv->weight, srv->port );
9759 err = DataBuffer_Append( inDB, &srvFields, sizeof( srvFields ) );
9760 require_noerr( err, exit );
9761
9762 // Append SRV RDATA target non-root labels to response.
9763
9764 if( srv->targetLen > 0 )
9765 {
9766 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
9767 require_noerr( err, exit );
9768 }
9769
9770 if( srvDomainLen > 0 )
9771 {
9772 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
9773 require_noerr( err, exit );
9774 }
9775
9776 // Append SRV RDATA target root label to response.
9777
9778 err = DataBuffer_Append( inDB, "", 1 );
9779 require_noerr( err, exit );
9780
9781 ++answerCount;
9782 }
9783 }
9784 else if( inQType == kDNSServiceType_SOA )
9785 {
9786 size_t nameLen, recordLen;
9787
9788 require_action_quiet( nameFlags & kDNSNameFlag_HasSOA, done, status = kQueryStatus_OK );
9789
9790 nameLen = DomainNameLength( me->domain );
9791 if( !inForTCP )
9792 {
9793 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
9794 require_noerr( err, exit );
9795
9796 // If the transport is UDP, make sure the message is within the UDP size limit.
9797
9798 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
9799 {
9800 status = kQueryStatus_Truncated;
9801 goto done;
9802 }
9803 }
9804
9805 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
9806 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
9807 me->defaultTTL, NULL );
9808 require_noerr( err, exit );
9809
9810 ++answerCount;
9811 }
9812 else if( inQType == kDNSServiceType_PTR )
9813 {
9814 dns_fixed_fields_record recFields;
9815 size_t domainLen, rdataLen, recordLen;
9816 uint8_t label[ 1 + kDomainLabelLengthMax ];
9817 uint8_t * dst = &label[ 1 ];
9818 const uint8_t * lim = &label[ countof( label ) ];
9819
9820 if( nameFlags & kDNSNameFlag_HasPTRv4 )
9821 {
9822 size_t maxLen;
9823 int n;
9824 const uint32_t ipv4Addr = kDNSServerBaseAddrV4 + hostID;
9825
9826 maxLen = (size_t)( lim - dst );
9827 n = MemPrintF( dst, maxLen, "ipv4-%u-%u-%u-%u",
9828 ( ipv4Addr >> 24 ) & 0xFFU,
9829 ( ipv4Addr >> 16 ) & 0xFFU,
9830 ( ipv4Addr >> 8 ) & 0xFFU,
9831 ipv4Addr & 0xFFU );
9832 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv4 hostname label" );
9833 dst += n;
9834 }
9835 else if( nameFlags & kDNSNameFlag_HasPTRv6 )
9836 {
9837 size_t maxLen;
9838 int n, i;
9839 uint8_t ipv6Addr[ 16 ];
9840
9841 maxLen = (size_t)( lim - dst );
9842 n = MemPrintF( dst, maxLen, "ipv6" );
9843 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv6 hostname label" );
9844 dst += n;
9845
9846 memcpy( ipv6Addr, kDNSServerBaseAddrV6, 16 );
9847 ipv6Addr[ 15 ] = (uint8_t) hostID;
9848 for( i = 0; i < 8; ++i )
9849 {
9850 maxLen = (size_t)( lim - dst );
9851 n = MemPrintF( dst, maxLen, "-%04x", ReadBig16( &ipv6Addr[ i * 2 ] ) );
9852 require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv6 hostname label" );
9853 dst += n;
9854 }
9855 }
9856 else
9857 {
9858 status = kQueryStatus_OK;
9859 goto done;
9860 }
9861 label[ 0 ] = (uint8_t)( dst - &label[ 1 ] );
9862
9863 // If the transport is UDP, make sure the message is within the UDP size limit.
9864
9865 domainLen = DomainNameLength( me->domain );
9866 rdataLen = 1 + label[ 0 ] + domainLen;
9867 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9868 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9869 {
9870 status = kQueryStatus_Truncated;
9871 goto done;
9872 }
9873
9874 // Append PTR record NAME to response.
9875
9876 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9877 require_noerr( err, exit );
9878
9879 // Append PTR record TYPE, CLASS, TTL, and RDLENGTH to response.
9880
9881 dns_fixed_fields_record_init( &recFields, kDNSServiceType_PTR, kDNSServiceClass_IN, me->defaultTTL,
9882 (uint16_t) rdataLen );
9883 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9884 require_noerr( err, exit );
9885
9886 // Append PTR record RDATA to response.
9887
9888 err = DataBuffer_Append( inDB, label, 1 + label[ 0 ] );
9889 require_noerr( err, exit );
9890
9891 err = DataBuffer_Append( inDB, me->domain, domainLen );
9892 require_noerr( err, exit );
9893
9894 ++answerCount;
9895 }
9896 else if( inQType == kDNSServiceType_DNSKEY )
9897 {
9898 size_t recordLen;
9899 size_t signerLen = 0;
9900 dns_fixed_fields_record recFields;
9901 dns_fixed_fields_rrsig sigFields;
9902 Boolean needSig;
9903
9904 require_action_quiet( nameFlags & kDNSNameFlag_HasDNSKEY, done, status = kQueryStatus_OK );
9905
9906 // If the transport is UDP, make sure the message is within the UDP size limit.
9907
9908 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + DNSKeyInfoGetRDataLen( zsk );
9909 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9910 {
9911 status = kQueryStatus_Truncated;
9912 goto done;
9913 }
9914 // Append ZSK DNSKEY record NAME to response.
9915
9916 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9917 require_noerr( err, exit );
9918
9919 // Append ZSK DNSKEY record TYPE, CLASS, TTL, and RDLENGTH to response.
9920
9921 dns_fixed_fields_record_init( &recFields, kDNSServiceType_DNSKEY, kDNSServiceClass_IN, ttl,
9922 DNSKeyInfoGetRDataLen( zsk ) );
9923 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9924 require_noerr( err, exit );
9925
9926 // Append ZSK DNSKEY record RDATA to response.
9927
9928 err = DataBuffer_Append( inDB, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
9929 require_noerr( err, exit );
9930
9931 ++answerCount;
9932
9933 needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9934 if( needSig )
9935 {
9936 uint32_t inceptionSecs;
9937 int labelCount;
9938
9939 // Initialize signing buffer.
9940
9941 check( !sigMsg );
9942 sigMsg = &sigDB;
9943 DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9944
9945 // Append RRSIG record RDATA fixed fields to signing buffer.
9946
9947 memset( &sigFields, 0, sizeof( sigFields ) );
9948 dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_DNSKEY );
9949 dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( ksk ) );
9950 labelCount = DomainNameLabelCount( ownerLower );
9951 check( labelCount >= 0 );
9952 dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9953 dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
9954 inceptionSecs = (uint32_t) now.tv_sec;
9955 dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9956 dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9957 dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( ksk ) );
9958
9959 err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9960 require_noerr( err, exit );
9961
9962 // Append RRSIG record RDATA signer to signing buffer.
9963
9964 signerLen = DomainNameLength( zone );
9965 err = DataBuffer_Append( sigMsg, zone, signerLen );
9966 require_noerr( err, exit );
9967
9968 // Append ZSK DNSKEY record to signing buffer.
9969
9970 err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
9971 require_noerr( err, exit );
9972
9973 err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9974 require_noerr( err, exit );
9975
9976 err = DataBuffer_Append( sigMsg, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
9977 require_noerr( err, exit );
9978 }
9979 // If the transport is UDP, make sure the message is within the UDP size limit.
9980
9981 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + DNSKeyInfoGetRDataLen( ksk );
9982 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9983 {
9984 status = kQueryStatus_Truncated;
9985 goto done;
9986 }
9987 // Append KSK DNSKEY record NAME to response.
9988
9989 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9990 require_noerr( err, exit );
9991
9992 // Append KSK DNSKEY record TYPE, CLASS, TTL, and RDLENGTH to response.
9993
9994 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9995 require_noerr( err, exit );
9996
9997 // Append KSK DNSKEY record RDATA to response.
9998
9999 err = DataBuffer_Append( inDB, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10000 require_noerr( err, exit );
10001
10002 ++answerCount;
10003 if( needSig )
10004 {
10005 size_t rdataLen;
10006 uint8_t signature[ kDNSServerSignatureLengthMax ];
10007 size_t signatureLen;
10008 Boolean didSign;
10009
10010 // Append KSK DNSKEY record to signing buffer.
10011
10012 err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
10013 require_noerr( err, exit );
10014
10015 err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
10016 require_noerr( err, exit );
10017
10018 err = DataBuffer_Append( sigMsg, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10019 require_noerr( err, exit );
10020
10021 // Compute signature with KSK.
10022
10023 memset( signature, 0, sizeof( signature ) );
10024 didSign = DNSKeyInfoSign( ksk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
10025 signature, &signatureLen );
10026 require_quiet( didSign, exit );
10027
10028 #if( DEBUG )
10029 _DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
10030 signatureLen, ksk );
10031 #endif
10032 // If the transport is UDP, make sure the message is within the UDP size limit.
10033
10034 rdataLen = sizeof( sigFields ) + signerLen + signatureLen;
10035 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10036 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10037 {
10038 status = kQueryStatus_Truncated;
10039 goto done;
10040 }
10041 // Append RRSIG record NAME to response.
10042
10043 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10044 require_noerr( err, exit );
10045
10046 // Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
10047
10048 dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10049 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10050 require_noerr( err, exit );
10051
10052 // Append RRSIG record RDATA fixed fields and signer to response.
10053
10054 err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
10055 require_noerr( err, exit );
10056
10057 err = DataBuffer_Append( inDB, zone, signerLen );
10058 require_noerr( err, exit );
10059
10060 // Append RRSIG record RDATA signature to response.
10061
10062 err = DataBuffer_Append( inDB, signature, signatureLen );
10063 require_noerr( err, exit );
10064
10065 ++answerCount;
10066 DataBuffer_Free( sigMsg );
10067 sigMsg = NULL;
10068 }
10069 }
10070 else if( inQType == kDNSServiceType_DS )
10071 {
10072 SHA256_CTX ctx;
10073 size_t rdataLen, i, recordLen;
10074 size_t signerLen = 0;
10075 dns_ds_sha256 * dsPtr;
10076 dns_ds_sha256 * dsPtrs[ 2 ];
10077 int cmp;
10078 dns_ds_sha256 dsZSK, dsKSK;
10079 dns_fixed_fields_rrsig sigFields;
10080 dns_fixed_fields_record recFields;
10081 Boolean needSig;
10082
10083 check_compile_time_code( sizeof( dsPtr->digest ) == SHA256_DIGEST_LENGTH );
10084
10085 require_action_quiet( nameFlags & kDNSNameFlag_HasDS, done, status = kQueryStatus_OK );
10086
10087 // Set up ZSK DS RDATA.
10088
10089 dsPtr = &dsZSK;
10090 memset( dsPtr, 0, sizeof( *dsPtr ) );
10091 dns_ds_sha256_set_key_tag( dsPtr, DNSKeyInfoGetKeyTag( zsk ) );
10092 dns_ds_sha256_set_algorithm( dsPtr, DNSKeyInfoGetAlgorithm( zsk ) );
10093 dns_ds_sha256_set_digest_type( dsPtr, kDSDigestType_SHA256 );
10094
10095 SHA256_Init( &ctx );
10096 SHA256_Update( &ctx, ownerLower, ownerLowerLen );
10097 SHA256_Update( &ctx, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
10098 SHA256_Final( dsPtr->digest, &ctx );
10099
10100 // Set up KSK DS RDATA.
10101
10102 dsPtr = &dsKSK;
10103 memset( dsPtr, 0, sizeof( *dsPtr ) );
10104 dns_ds_sha256_set_key_tag( dsPtr, DNSKeyInfoGetKeyTag( ksk ) );
10105 dns_ds_sha256_set_algorithm( dsPtr, DNSKeyInfoGetAlgorithm( ksk ) );
10106 dns_ds_sha256_set_digest_type( dsPtr, kDSDigestType_SHA256 );
10107
10108 SHA256_Init( &ctx );
10109 SHA256_Update( &ctx, ownerLower, ownerLowerLen );
10110 SHA256_Update( &ctx, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10111 SHA256_Final( dsPtr->digest, &ctx );
10112
10113 // Order the DS RDATAs
10114
10115 cmp = memcmp( &dsZSK, &dsKSK, sizeof( dns_ds_sha256 ) );
10116 if( cmp <= 0 )
10117 {
10118 dsPtrs[ 0 ] = &dsZSK;
10119 dsPtrs[ 1 ] = ( cmp == 0 ) ? NULL : &dsKSK;
10120 }
10121 else
10122 {
10123 dsPtrs[ 0 ] = &dsKSK;
10124 dsPtrs[ 1 ] = &dsZSK;
10125 }
10126 needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
10127 if( needSig )
10128 {
10129 uint32_t inceptionSecs;
10130 int labelCount;
10131
10132 // Initialize signing buffer.
10133
10134 check( !sigMsg );
10135 sigMsg = &sigDB;
10136 DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
10137
10138 // Append RRSIG record RDATA fixed fields to signing buffer.
10139
10140 memset( &sigFields, 0, sizeof( sigFields ) );
10141 dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_DS );
10142 dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zskParent ) );
10143 labelCount = DomainNameLabelCount( ownerLower );
10144 check( labelCount >= 0 );
10145 dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
10146 dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
10147 inceptionSecs = (uint32_t) now.tv_sec;
10148 dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
10149 dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
10150 dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zskParent ) );
10151
10152 err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
10153 require_noerr( err, exit );
10154
10155 // Append RRSIG record RDATA signer to signing buffer.
10156
10157 signerLen = DomainNameLength( zoneParent );
10158 err = DataBuffer_Append( sigMsg, zoneParent, signerLen );
10159 require_noerr( err, exit );
10160 }
10161 rdataLen = sizeof( dns_ds_sha256 );
10162 dns_fixed_fields_record_init( &recFields, kDNSServiceType_DS, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10163 for( i = 0; i < countof( dsPtrs ); ++i )
10164 {
10165 dsPtr = dsPtrs[ i ];
10166 if( !dsPtr ) continue;
10167
10168 // If the transport is UDP, make sure the message is within the UDP size limit.
10169
10170 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10171 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10172 {
10173 status = kQueryStatus_Truncated;
10174 goto done;
10175 }
10176 // Append DS record NAME to response.
10177
10178 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10179 require_noerr( err, exit );
10180
10181 // Append DS record TYPE, CLASS, TTL, and RDLENGTH to response.
10182 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10183 require_noerr( err, exit );
10184
10185 // Append DS record RDATA to response.
10186
10187 err = DataBuffer_Append( inDB, dsPtr, sizeof( *dsPtr ) );
10188 require_noerr( err, exit );
10189
10190 ++answerCount;
10191 if( needSig )
10192 {
10193 // Append DS record to signing buffer.
10194
10195 err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
10196 require_noerr( err, exit );
10197
10198 err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
10199 require_noerr( err, exit );
10200
10201 err = DataBuffer_Append( sigMsg, dsPtr, sizeof( *dsPtr ) );
10202 require_noerr( err, exit );
10203 }
10204 }
10205 if( needSig )
10206 {
10207 uint8_t signature[ kDNSServerSignatureLengthMax ];
10208 size_t signatureLen;
10209 Boolean didSign;
10210
10211 // Compute signature with parent ZSK.
10212
10213 memset( signature, 0, sizeof( signature ) );
10214 didSign = DNSKeyInfoSign( zskParent, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
10215 signature, &signatureLen );
10216 require_quiet( didSign, exit );
10217
10218 #if( DEBUG )
10219 _DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
10220 signatureLen, zskParent );
10221 #endif
10222 // If the transport is UDP, make sure the message is within the UDP size limit.
10223
10224 rdataLen = sizeof( sigFields ) + signerLen + signatureLen;
10225 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10226 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10227 {
10228 status = kQueryStatus_Truncated;
10229 goto done;
10230 }
10231 // Append RRSIG record NAME to response.
10232
10233 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10234 require_noerr( err, exit );
10235
10236 // Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
10237
10238 dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10239 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10240 require_noerr( err, exit );
10241
10242 // Append RRSIG record RDATA fixed fields and signer to response.
10243
10244 err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
10245 require_noerr( err, exit );
10246
10247 err = DataBuffer_Append( inDB, zoneParent, signerLen );
10248 require_noerr( err, exit );
10249
10250 // Append RRSIG record RDATA signature to response.
10251
10252 err = DataBuffer_Append( inDB, signature, signatureLen );
10253 require_noerr( err, exit );
10254
10255 ++answerCount;
10256
10257 DataBuffer_Free( sigMsg );
10258 sigMsg = NULL;
10259 }
10260 }
10261 status = kQueryStatus_OK;
10262
10263 done:
10264 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
10265 flags = DNSHeaderGetFlags( hdr );
10266 switch( status )
10267 {
10268 case kQueryStatus_OK:
10269 case kQueryStatus_Truncated:
10270 flags |= kDNSHeaderFlag_AuthAnswer;
10271 if( status == kQueryStatus_Truncated ) flags |= kDNSHeaderFlag_Truncation;
10272 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
10273 break;
10274
10275 case kQueryStatus_NotImplemented:
10276 rcode = kDNSRCode_NotImp;
10277 break;
10278
10279 case kQueryStatus_Refused:
10280 rcode = kDNSRCode_Refused;
10281 break;
10282
10283 case kQueryStatus_Null:
10284 default:
10285 err = kInternalErr;
10286 goto exit;
10287 }
10288 if( rcodeOverride >= 0 )
10289 {
10290 dns_fixed_fields_record recFields;
10291 const char * rcodeStr;
10292 size_t maxLen, txtStrLen, rdataLen, recordLen;
10293 int n;
10294 uint8_t rdataBuf[ 1 + 255 ]; // Enough space for one TXT record string.
10295
10296 // Create the RDATA for an informational TXT record that contains the original rcode to put in the Additional
10297 // section.
10298
10299 maxLen = sizeof( rdataBuf ) - 1;
10300 rcodeStr = DNSRCodeToString( rcode );
10301 if( rcodeStr ) n = MemPrintF( &rdataBuf[ 1 ], maxLen, "original-rcode=%s", rcodeStr );
10302 else n = MemPrintF( &rdataBuf[ 1 ], maxLen, "original-rcode=%d", rcode );
10303 txtStrLen = ( n > 0 ) ? Min( (size_t) n, maxLen ) : 0;
10304 rdataBuf[ 0 ] = (uint8_t) txtStrLen;
10305 rdataLen = 1 + txtStrLen;
10306
10307 // The TXT record isn't strictly necessary, so only include it if it fits.
10308
10309 recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10310 maxLen = inForTCP ? kDNSMaxTCPMessageSize : kDNSMaxUDPMessageSize;
10311 if( ( DataBuffer_GetLen( inDB ) < maxLen ) && ( ( maxLen - DataBuffer_GetLen( inDB ) ) >= recordLen ) )
10312 {
10313 // Append TXT record NAME to response.
10314
10315 err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10316 require_noerr( err, exit );
10317
10318 // Append TXT record TYPE, CLASS, TTL, and RDLENGTH to response.
10319
10320 dns_fixed_fields_record_init( &recFields, kDNSRecordType_TXT, kDNSClassType_IN, 0, (uint16_t) rdataLen );
10321 err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10322 require_noerr( err, exit );
10323
10324 // Append TXT record RDATA to response.
10325
10326 err = DataBuffer_Append( inDB, rdataBuf, rdataLen );
10327 require_noerr( err, exit );
10328
10329 ++additionalCount;
10330 }
10331 rcode = rcodeOverride;
10332 }
10333 DNSFlagsSetRCode( flags, rcode );
10334 DNSHeaderSetFlags( hdr, flags );
10335 DNSHeaderSetAnswerCount( hdr, answerCount );
10336 DNSHeaderSetAdditionalCount( hdr, additionalCount );
10337 if( procDelayMs > 0 )
10338 {
10339 const uint64_t delayTicks = MillisecondsToUpTicks( procDelayMs );
10340 const uint64_t elapsedTicks = UpTicks() - startTicks;
10341
10342 if( delayTicks > elapsedTicks ) SleepForUpTicks( delayTicks - elapsedTicks );
10343 }
10344 err = kNoErr;
10345
10346 exit:
10347 FreeNullSafe( qnameLower );
10348 if( sigMsg ) DataBuffer_Free( sigMsg );
10349 return( err );
10350 }
10351
10352 static Boolean
10353 _DNSServerNameIsDNSSECZone(
10354 const uint8_t * inName,
10355 const uint8_t ** outZoneParent,
10356 DNSKeyInfoRef * outZSK,
10357 DNSKeyInfoRef * outKSK,
10358 DNSKeyInfoRef * outParentZSK );
10359
10360 static Boolean
10361 _DNSServerParseHostName(
10362 DNSServerRef me,
10363 const uint8_t * inQName,
10364 uint32_t * outAliasCount,
10365 uint32_t outAliasTTLs[ kAliasTTLCountMax ],
10366 uint32_t * outAliasTTLCount,
10367 uint32_t * outCount,
10368 uint32_t * outRandCount,
10369 uint32_t * outIndex,
10370 int * outRCode,
10371 uint32_t * outTTL,
10372 uint32_t * outProcDelayMs,
10373 DNSNameFlags * outFlags,
10374 const uint8_t ** outZone,
10375 const uint8_t ** outZoneParent,
10376 DNSKeyInfoRef * outZSK,
10377 DNSKeyInfoRef * outKSK,
10378 DNSKeyInfoRef * outParentZSK )
10379 {
10380 OSStatus err;
10381 const uint8_t * label;
10382 size_t labelLen;
10383 const uint8_t * labelNext;
10384 uint32_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
10385 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
10386 int32_t count = -1; // First arg from Count label. Valid values are in [0, 255].
10387 uint32_t randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
10388 uint32_t index = 0; // Arg from Index label. Valid values are in [1, 2^32 - 1].
10389 int rcode = -1; // Arg from RCode label. Valid values are in [0, 15].
10390 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
10391 uint32_t procDelayMs = 0; // Arg from PDelay label. Valid values are in [1, 2000]. Units are in ms.
10392 DNSNameFlags flags = 0;
10393 int32_t maxCount;
10394 const uint8_t * zone = NULL;
10395 const uint8_t * zoneParent = NULL;
10396 DNSKeyInfoRef zsk = NULL;
10397 DNSKeyInfoRef ksk = NULL;
10398 DNSKeyInfoRef parentZSK = NULL;
10399 Boolean isAlias = false;
10400
10401 for( label = inQName; ( labelLen = *label ) != 0; label = labelNext )
10402 {
10403 const uint8_t * labelData;
10404 uint32_t arg;
10405
10406 if( labelLen > kDomainLabelLengthMax ) break;
10407 labelData = &label[ 1 ];
10408 labelNext = &labelData[ labelLen ];
10409
10410 if( label == inQName )
10411 {
10412 // Check if the first label is a valid alias TTL sequence label.
10413 // Note: Since "alias" is a prefix of "alias-ttl", check for "alias-ttl" first.
10414
10415 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_AliasTTL ) == 0 )
10416 {
10417 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_AliasTTL ) ];
10418 const char * const end = (const char *) labelNext;
10419
10420 while( ptr < end )
10421 {
10422 if( *ptr++ != '-' ) break;
10423 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10424 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
10425 if( outAliasTTLs ) outAliasTTLs[ aliasTTLCount ] = arg;
10426 ++aliasTTLCount;
10427 }
10428 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
10429 isAlias = true;
10430 continue;
10431 }
10432
10433 // Check if the first label is a valid alias label.
10434
10435 if( ( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Alias ) == 0 ) )
10436 {
10437 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Alias ) ];
10438 const char * const end = (const char *) labelNext;
10439
10440 if( ptr < end )
10441 {
10442 if( *ptr++ != '-' ) break;
10443 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10444 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
10445 aliasCount = arg;
10446 if( ptr != end ) break;
10447 }
10448 else
10449 {
10450 aliasCount = 1;
10451 }
10452 isAlias = true;
10453 continue;
10454 }
10455 }
10456
10457 // Check if this label is a valid count label.
10458
10459 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Count ) == 0 )
10460 {
10461 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Count ) ];
10462 const char * const end = (const char *) labelNext;
10463
10464 if( count >= 0 ) break; // Count cannot be specified more than once.
10465
10466 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10467 if( err || ( arg > INT32_MAX ) ) break; // The actual upper bound for Count will be verified below.
10468 count = (int32_t) arg;
10469
10470 if( ptr < end )
10471 {
10472 if( *ptr++ != '-' ) break;
10473 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10474 if( err || ( arg < ( (uint32_t) count ) ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
10475 randCount = arg;
10476 if( ptr != end ) break;
10477 }
10478 continue;
10479 }
10480
10481 // Check if this label is a valid Index label.
10482
10483 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Index ) == 0 )
10484 {
10485 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Index ) ];
10486 const char * const end = (const char *) labelNext;
10487
10488 if( index > 0 ) break; // Index cannot be specified more than once.
10489
10490 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10491 if( err || ( arg < 1 ) || ( arg > UINT32_MAX ) ) break; // Index must be in [1, 2^32 - 1].
10492 index = arg;
10493 if( ptr != end ) break;
10494 continue;
10495 }
10496
10497 // Check if this label is a valid IPv4 label.
10498
10499 if( strnicmpx( labelData, labelLen, kLabel_IPv4 ) == 0 )
10500 {
10501 // Valid names have at most one IPv4 or IPv6 label.
10502
10503 if( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) break;
10504 flags |= kDNSNameFlag_HasA;
10505 continue;
10506 }
10507
10508 // Check if this label is a valid IPv6 label.
10509
10510 if( strnicmpx( labelData, labelLen, kLabel_IPv6 ) == 0 )
10511 {
10512 // Valid names have at most one IPv4 or IPv6 label.
10513
10514 if( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) break;
10515 flags |= kDNSNameFlag_HasAAAA;
10516 continue;
10517 }
10518
10519 // Check if this label is a valid tag label.
10520
10521 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Tag ) == 0 )
10522 {
10523 continue;
10524 }
10525
10526 // Check if this label is a valid RCode label.
10527
10528 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_RCode ) == 0 )
10529 {
10530 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_RCode ) ];
10531 const char * const end = (const char *) labelNext;
10532 const char * src;
10533 char * dst;
10534 const char * lim;
10535 char argStr[ kDomainLabelLengthMax + 1 ];
10536
10537 if( rcode >= 0 ) break; // RCode cannot be specified more than once.
10538
10539 // First check if the RCode label's argument is an RCODE mnemonic, e.g., ServFail, Refused, etc.
10540 // The argument part of a label consists of all of the characters up to the start of the next label.
10541 // In order to treat the argument as a C string, the argument must not contain any NUL characters.
10542 // For example, a malformed label such as "rcode-refused\x00garbage" has the argument "refused\x00garbage",
10543 // but as a C string, the NUL character makes it "refused".
10544
10545 src = ptr;
10546 dst = argStr;
10547 lim = &argStr[ countof( argStr ) - 1 ];
10548 while( ( src < end ) && ( *src != '\0' ) && ( dst < lim ) ) *dst++ = *src++;
10549 if( src == end )
10550 {
10551 *dst = '\0';
10552 rcode = DNSRCodeFromString( argStr );
10553 }
10554
10555 // If we don't have a valid rcode yet, try to parse the argument as a decimal integer.
10556
10557 if( rcode < 0 )
10558 {
10559 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10560 if( err || ( arg > 15 ) ) break; // RCode must be in [0, 15].
10561 rcode = (int) arg;
10562 if( ptr != end ) break;
10563 }
10564 continue;
10565 }
10566
10567 // Check if this label is a valid TTL label.
10568
10569 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_TTL ) == 0 )
10570 {
10571 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_TTL ) ];
10572 const char * const end = (const char *) labelNext;
10573
10574 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
10575
10576 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10577 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
10578 ttl = (int32_t) arg;
10579 if( ptr != end ) break;
10580 continue;
10581 }
10582
10583 // Check if this label is a valid PDelay label.
10584
10585 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_PDelay ) == 0 )
10586 {
10587 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_PDelay ) ];
10588 const char * const end = (const char *) labelNext;
10589
10590 if( procDelayMs > 0 ) break; // PDelay cannot be specified more than once.
10591
10592 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10593 if( err || ( arg < 1 ) || ( arg > 2000 ) ) break; // PDelay must be in [1, 2000].
10594 procDelayMs = arg;
10595 if( ptr != end ) break;
10596 continue;
10597 }
10598
10599 // If this and the remaining labels are equal to "d.test.", then the name exists.
10600 // Otherwise, this label is invalid. In both cases, there are no more labels to check.
10601
10602 if( _DNSServerNameIsDNSSECZone( label, &zoneParent, &zsk, &ksk, &parentZSK ) )
10603 {
10604 zone = label;
10605 flags |= kDNSNameFlag_HasRRSIG;
10606 if( ( label == inQName ) || ( isAlias && ( label == DomainNameGetNextLabel( inQName ) ) ) )
10607 {
10608 flags |= kDNSNameFlag_HasSOA;
10609 flags |= kDNSNameFlag_HasDNSKEY;
10610 flags |= kDNSNameFlag_HasDS;
10611 }
10612 }
10613 else if( DomainNameEqual( label, me->domain ) )
10614 {
10615 zone = label;
10616 if( ( label == inQName ) || ( isAlias && ( label == DomainNameGetNextLabel( inQName ) ) ) )
10617 {
10618 flags |= kDNSNameFlag_HasSOA;
10619 }
10620 }
10621 break;
10622 }
10623 require_quiet( zone, exit );
10624
10625 // If a Count value of 0 was specified, then the hostname has no A or AAAA records.
10626 // Otherwise, if the hostname has no IPv4 or IPv6 labels, then it has both A and AAAA records.
10627
10628 if( count == 0 )
10629 {
10630 flags &= ~( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA );
10631 }
10632 else if( !( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) )
10633 {
10634 flags |= ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA );
10635 }
10636
10637 // Allow IPv6-only hostnames to have a maximum address count of up to 511 instead of the normal 255.
10638
10639 maxCount = ( ( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) == kDNSNameFlag_HasAAAA ) ? 511 : 255;
10640 require_action_quiet( count <= maxCount, exit, zone = NULL );
10641
10642 if( outAliasCount ) *outAliasCount = aliasCount;
10643 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
10644 if( outCount ) *outCount = ( count >= 0 ) ? ( (uint32_t) count ) : 1;
10645 if( outRandCount ) *outRandCount = randCount;
10646 if( outIndex ) *outIndex = index;
10647 if( outRCode ) *outRCode = rcode;
10648 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
10649 if( outProcDelayMs ) *outProcDelayMs = procDelayMs;
10650 if( outFlags ) *outFlags = flags;
10651 if( outZone ) *outZone = zone;
10652 if( outZoneParent ) *outZoneParent = zoneParent;
10653 if( outZSK ) *outZSK = zsk;
10654 if( outKSK ) *outKSK = ksk;
10655 if( outParentZSK ) *outParentZSK = parentZSK;
10656
10657 exit:
10658 return( zone ? true : false );
10659 }
10660
10661 //===========================================================================================================================
10662
10663 static Boolean
10664 _DNSServerParseSRVName(
10665 DNSServerRef me,
10666 const uint8_t * inName,
10667 const uint8_t ** outDomainPtr,
10668 size_t * outDomainLen,
10669 ParsedSRV outSRVArray[ kParsedSRVCountMax ],
10670 size_t * outSRVCount )
10671 {
10672 OSStatus err;
10673 const uint8_t * label;
10674 const uint8_t * domainPtr;
10675 size_t domainLen;
10676 size_t srvCount;
10677 uint32_t arg;
10678 int isNameValid = false;
10679
10680 label = inName;
10681
10682 // Ensure that first label, i.e, the service label, begins with a '_' character.
10683
10684 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
10685 label = DomainNameGetNextLabel( label );
10686
10687 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
10688
10689 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
10690 label = DomainNameGetNextLabel( label );
10691
10692 // Parse the domain name, if any.
10693
10694 domainPtr = label;
10695 while( *label )
10696 {
10697 if( DomainNameEqual( label, me->domain ) ||
10698 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
10699 label = DomainNameGetNextLabel( label );
10700 }
10701 require_quiet( *label, exit );
10702
10703 domainLen = (size_t)( label - domainPtr );
10704
10705 // Parse SRV labels, if any.
10706
10707 srvCount = 0;
10708 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
10709 {
10710 const uint8_t * const nextLabel = DomainNameGetNextLabel( label );
10711 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
10712 const char * const end = (const char *) nextLabel;
10713 const uint8_t * target;
10714 unsigned int priority, weight, port;
10715
10716 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10717 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10718 priority = (unsigned int) arg;
10719
10720 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10721 ++ptr;
10722
10723 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10724 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10725 weight = (unsigned int) arg;
10726
10727 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10728 ++ptr;
10729
10730 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10731 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10732 port = (unsigned int) arg;
10733
10734 require_quiet( ptr == end, exit );
10735
10736 target = nextLabel;
10737 for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
10738 {
10739 if( DomainNameEqual( label, me->domain ) ||
10740 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
10741 }
10742 require_quiet( *label, exit );
10743
10744 if( outSRVArray )
10745 {
10746 outSRVArray[ srvCount ].priority = (uint16_t) priority;
10747 outSRVArray[ srvCount ].weight = (uint16_t) weight;
10748 outSRVArray[ srvCount ].port = (uint16_t) port;
10749 outSRVArray[ srvCount ].targetPtr = target;
10750 outSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
10751 }
10752 ++srvCount;
10753 }
10754 require_quiet( DomainNameEqual( label, me->domain ), exit );
10755 isNameValid = true;
10756
10757 if( outDomainPtr ) *outDomainPtr = domainPtr;
10758 if( outDomainLen ) *outDomainLen = domainLen;
10759 if( outSRVCount ) *outSRVCount = srvCount;
10760
10761 exit:
10762 return( isNameValid ? true : false );
10763 }
10764
10765 //===========================================================================================================================
10766
10767 static Boolean _DNSServerParseReverseIPv4Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID )
10768 {
10769 OSStatus err;
10770 const uint8_t * label;
10771 size_t labelLen;
10772 const uint8_t * labelData;
10773 const uint8_t * labelNext;
10774 const uint8_t * ptr;
10775 uint32_t hostID;
10776 int isNameValid = false;
10777
10778 Unused( me );
10779
10780 label = inQName;
10781 labelLen = *label;
10782 require_quiet( labelLen > 0, exit );
10783
10784 labelData = &label[ 1 ];
10785 labelNext = &labelData[ labelLen ];
10786 err = DecimalTextToUInt32( (const char *) labelData, (const char *) labelNext, &hostID, (const char **) &ptr );
10787 require_noerr_quiet( err, exit );
10788 require_quiet( ( hostID >= 1 ) && ( hostID <= 255 ), exit );
10789 require_quiet( ptr == labelNext, exit );
10790
10791 require_quiet( DomainNameEqual( labelNext, kDNSServerReverseIPv4DomainName ), exit );
10792 isNameValid = true;
10793
10794 if( outHostID ) *outHostID = (unsigned int) hostID;
10795
10796 exit:
10797 return( isNameValid ? true : false );
10798 }
10799
10800 //===========================================================================================================================
10801
10802 static Boolean _DNSServerParseReverseIPv6Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID )
10803 {
10804 const uint8_t * label;
10805 unsigned int hostID;
10806 int i;
10807 int isNameValid = false;
10808
10809 Unused( me );
10810
10811 hostID = 0;
10812 label = inQName;
10813 for( i = 0; i < 2; ++i )
10814 {
10815 unsigned int labelLen, c;
10816
10817 labelLen = label[ 0 ];
10818 require_quiet( labelLen == 1, exit );
10819
10820 c = label[ 1 ];
10821 require_quiet( isxdigit_safe( c ), exit );
10822
10823 hostID = hostID | ( HexCharToValue( c ) << ( 4 * i ) );
10824 label = &label[ 1 + labelLen ];
10825 }
10826 require_quiet( ( hostID >= 1 ) && ( hostID <= 255 ), exit );
10827 require_quiet( DomainNameEqual( label, kDNSServerReverseIPv6DomainName ), exit );
10828 isNameValid = true;
10829
10830 if( outHostID ) *outHostID = hostID;
10831
10832 exit:
10833 return( isNameValid ? true : false );
10834 }
10835
10836 #if( DEBUG )
10837 //===========================================================================================================================
10838
10839 static void
10840 _DNSServerSigCheck(
10841 const uint8_t * inOwner,
10842 int inTypeCovered,
10843 const void * inMsgPtr,
10844 size_t inMsgLen,
10845 const uint8_t * inSignaturePtr,
10846 const size_t inSignatureLen,
10847 DNSKeyInfoRef inKeyInfo )
10848 {
10849 if( !DNSKeyInfoVerify( inKeyInfo, inMsgPtr, inMsgLen, inSignaturePtr, inSignatureLen ) )
10850 {
10851 const char * typeStr;
10852 char typeBuf[ 16 ];
10853
10854 typeStr = DNSRecordTypeValueToString( inTypeCovered );
10855 if( !typeStr )
10856 {
10857 SNPrintF( typeBuf, sizeof( typeBuf ), "TYPE%d", inTypeCovered );
10858 typeStr = typeBuf;
10859 }
10860 ds_ulog( kLogLevelError,
10861 "Signature for %{du:dname} %s is invalid! -- algorithm: %s (%d), public key: '%H'\n",
10862 inOwner, typeStr, DNSKeyInfoGetAlgorithmDescription( inKeyInfo ), DNSKeyInfoGetAlgorithm( inKeyInfo ),
10863 DNSKeyInfoGetPubKeyPtr( inKeyInfo ), DNSKeyInfoGetPubKeyLen( inKeyInfo ), SIZE_MAX );
10864 }
10865 }
10866 #endif
10867
10868 //===========================================================================================================================
10869
10870 #define kDNSServerDefaultDNSSECAlgorithm 14 // TODO: Think about adding an option for the default algorithm.
10871
10872 static Boolean
10873 _DNSServerNameIsDNSSECZone(
10874 const uint8_t * inName,
10875 const uint8_t ** outZoneParent,
10876 DNSKeyInfoRef * outZSK,
10877 DNSKeyInfoRef * outKSK,
10878 DNSKeyInfoRef * outParentZSK )
10879 {
10880 const uint8_t * label;
10881 size_t labelLen;
10882 const uint8_t * labelNext;
10883 const uint8_t * zoneParent = NULL;
10884 DNSKeyInfoRef zsk;
10885 DNSKeyInfoRef ksk;
10886 DNSKeyInfoRef parentZSK;
10887 uint32_t zoneAlgorithm = 0;
10888 uint32_t zoneIndex = 0;
10889 uint32_t zoneParentAlgorithm = 0;
10890 uint32_t zoneParentIndex = 0;
10891 Boolean parsedAllLabels = false;
10892 Boolean nameIsValid = false;
10893
10894 for( label = inName; ( labelLen = *label ) != 0; label = labelNext )
10895 {
10896 const uint8_t * labelData;
10897
10898 if( labelLen > kDomainLabelLengthMax ) break;
10899 labelData = &label[ 1 ];
10900 labelNext = &labelData[ labelLen ];
10901
10902 if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Zone ) == 0 )
10903 {
10904 OSStatus err;
10905 const char * ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Zone ) ];
10906 const char * const end = (const char *) labelNext;
10907 uint32_t algorithm;
10908 uint32_t index;
10909
10910 err = DecimalTextToUInt32( ptr, end, &algorithm, &ptr );
10911 if( err ) break;
10912 if( ( ptr >= end ) || ( *ptr++ != '-' ) ) break;
10913
10914 err = DecimalTextToUInt32( ptr, end, &index, &ptr );
10915 if( err || ( index < kZoneLabelIndexArgMin ) || ( index > kZoneLabelIndexArgMax ) ) break;
10916 if( ptr != end ) break;
10917 if( zoneIndex == 0 )
10918 {
10919 zoneAlgorithm = algorithm;
10920 zoneIndex = index;
10921 }
10922 else if( zoneParentIndex == 0 )
10923 {
10924 zoneParentAlgorithm = algorithm;
10925 zoneParent = label;
10926 zoneParentIndex = index;
10927 }
10928 continue;
10929 }
10930 if( DomainNameEqual( label, kDNSServerDomain_DNSSEC ) )
10931 {
10932 if( !zoneParent ) zoneParent = label;
10933 parsedAllLabels = true;
10934 }
10935 break;
10936 }
10937 require_quiet( parsedAllLabels, exit );
10938
10939 if( zoneAlgorithm == 0 ) zoneAlgorithm = kDNSServerDefaultDNSSECAlgorithm;
10940 zsk = GetDNSKeyInfoZSK( zoneAlgorithm, zoneIndex );
10941 require_quiet( zsk, exit );
10942
10943 ksk = GetDNSKeyInfoKSK( zoneAlgorithm, zoneIndex );
10944 require_quiet( ksk, exit );
10945
10946 if( zoneParentAlgorithm == 0 ) zoneParentAlgorithm = kDNSServerDefaultDNSSECAlgorithm;
10947 parentZSK = GetDNSKeyInfoZSK( zoneParentAlgorithm, zoneParentIndex );
10948 require_quiet( parentZSK, exit );
10949
10950 if( outZoneParent ) *outZoneParent = zoneParent;
10951 if( outZSK ) *outZSK = zsk;
10952 if( outKSK ) *outKSK = ksk;
10953 if( outParentZSK ) *outParentZSK = parentZSK;
10954 nameIsValid = true;
10955
10956 exit:
10957 return( nameIsValid );
10958 }
10959
10960 //===========================================================================================================================
10961
10962 static OSStatus
10963 _DNSServerConnectionCreate(
10964 DNSServerRef inServer,
10965 const struct sockaddr * inLocal,
10966 const struct sockaddr * inRemote,
10967 size_t inIndex,
10968 DNSServerConnectionRef * outCnx )
10969 {
10970 OSStatus err;
10971 DNSServerConnectionRef obj;
10972
10973 CF_OBJECT_CREATE( DNSServerConnection, obj, err, exit );
10974
10975 obj->index = inIndex;
10976 obj->server = inServer;
10977 CFRetain( obj->server );
10978 SockAddrCopy( inLocal, &obj->local );
10979 SockAddrCopy( inRemote, &obj->remote );
10980
10981 *outCnx = obj;
10982 err = kNoErr;
10983
10984 exit:
10985 return( err );
10986 }
10987
10988 //===========================================================================================================================
10989
10990 static void _DNSServerConnectionFinalize( CFTypeRef inObj )
10991 {
10992 const DNSServerConnectionRef me = (DNSServerConnectionRef) inObj;
10993
10994 check( !me->readSource );
10995 check( !me->writeSource );
10996 ForgetCF( &me->server );
10997 ForgetMem( &me->msgPtr );
10998 }
10999
11000 //===========================================================================================================================
11001
11002 static OSStatus _DNSServerConnectionStart( DNSServerConnectionRef me, SocketRef inSock )
11003 {
11004 OSStatus err;
11005 SocketContext * sockCtx = NULL;
11006
11007 err = SocketMakeNonBlocking( inSock );
11008 require_noerr( err, exit );
11009
11010 #if( defined( SO_NOSIGPIPE ) )
11011 setsockopt( inSock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
11012 #endif
11013 me->readSource = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, me->server->queue );
11014 require_action( me->readSource, exit, err = kNoResourcesErr );
11015 me->readSuspended = true;
11016
11017 me->writeSource = dispatch_source_create( DISPATCH_SOURCE_TYPE_WRITE, (uintptr_t) inSock, 0, me->server->queue );
11018 require_action( me->writeSource, exit, err = kNoResourcesErr );
11019 me->writeSuspended = true;
11020
11021 sockCtx = SocketContextCreateEx( inSock, me, SocketContextFinalizerCF, &err );
11022 require_noerr( err, exit );
11023 CFRetain( me );
11024
11025 SocketContextRetain( sockCtx );
11026 dispatch_set_context( me->readSource, sockCtx );
11027 dispatch_source_set_event_handler_f( me->readSource, _DNSServerConnectionReadHandler );
11028 dispatch_source_set_cancel_handler_f( me->readSource, SocketContextCancelHandler );
11029 dispatch_resume_if_suspended( me->readSource, &me->readSuspended );
11030
11031 SocketContextRetain( sockCtx );
11032 dispatch_set_context( me->writeSource, sockCtx );
11033 dispatch_source_set_event_handler_f( me->writeSource, _DNSServerConnectionWriteHandler );
11034 dispatch_source_set_cancel_handler_f( me->writeSource, SocketContextCancelHandler );
11035
11036 _DNSServerConnectionRenewExpiration( me );
11037
11038 exit:
11039 if( sockCtx ) SocketContextRelease( sockCtx );
11040 if( err ) _DNSServerConnectionStop( me, true );
11041 return( err );
11042 }
11043
11044 //===========================================================================================================================
11045
11046 static void _DNSServerConnectionStop( DNSServerConnectionRef me, Boolean inRemoveFromList )
11047 {
11048 dispatch_source_forget_ex( &me->readSource, &me->readSuspended );
11049 dispatch_source_forget_ex( &me->writeSource, &me->writeSuspended );
11050 if( inRemoveFromList )
11051 {
11052 DNSServerConnectionRef * ptr;
11053
11054 ptr = &me->server->connectionList;
11055 while( *ptr && ( *ptr != me ) ) ptr = &( *ptr )->next;
11056 if( *ptr )
11057 {
11058 *ptr = me->next;
11059 me->next = NULL;
11060 CFRelease( me );
11061 }
11062 }
11063 }
11064
11065 //===========================================================================================================================
11066
11067 static void _DNSServerConnectionReadHandler( void *inContext )
11068 {
11069 OSStatus err;
11070 const SocketContext * const sockCtx = (SocketContext *) inContext;
11071 const DNSServerConnectionRef me = (DNSServerConnectionRef) sockCtx->userContext;
11072 uint8_t * respPtr = NULL; // malloc'd
11073 size_t respLen;
11074
11075 // Receive message length.
11076
11077 if( !me->haveLen )
11078 {
11079 err = SocketReadData( sockCtx->sock, me->lenBuf, sizeof( me->lenBuf ), &me->offset );
11080 if( ( err == EWOULDBLOCK ) || ( err == kConnectionErr ) ) goto exit;
11081 require_noerr( err, exit );
11082
11083 me->haveLen = true;
11084 me->offset = 0;
11085 me->msgLen = ReadBig16( me->lenBuf );
11086 if( me->msgLen < kDNSHeaderLength )
11087 {
11088 ds_ulog( kLogLevelInfo, "TCP: Message length of %zu bytes from %##a to %##a is too small (< %d bytes)\n",
11089 me->msgLen, &me->remote, &me->local, kDNSHeaderLength );
11090 err = kSizeErr;
11091 goto exit;
11092 }
11093 me->msgPtr = malloc( me->msgLen );
11094 require_action( me->msgPtr, exit, err = kNoMemoryErr );
11095 }
11096
11097 // Receive message.
11098
11099 err = SocketReadData( sockCtx->sock, me->msgPtr, me->msgLen, &me->offset );
11100 if( ( err == EWOULDBLOCK ) || ( err == kConnectionErr ) ) goto exit;
11101 require_noerr( err, exit );
11102 dispatch_suspend_if_resumed( me->readSource, &me->readSuspended );
11103 me->offset = 0;
11104 me->haveLen = false;
11105
11106 ds_ulog( kLogLevelInfo, "TCP: Received %zu bytes from %##a to %##a -- %.1{du:dnsmsg}\n",
11107 me->msgLen, &me->remote, &me->local, me->msgPtr, me->msgLen );
11108
11109 // Create response.
11110
11111 err = _DNSServerAnswerQueryForTCP( me->server, me->msgPtr, me->msgLen, me->index + 1, &respPtr, &respLen );
11112 if( err == kSkipErr ) ds_ulog( kLogLevelInfo, "TCP: Ignoring query\n" );
11113 require_noerr_quiet( err, exit );
11114
11115 _DNSServerConnectionRenewExpiration( me );
11116
11117 // Prepare to send response.
11118
11119 FreeNullSafe( me->msgPtr );
11120 me->msgPtr = respPtr;
11121 me->msgLen = respLen;
11122 respPtr = NULL;
11123
11124 ds_ulog( kLogLevelInfo, "TCP: Sending %zu byte response from %##a to %##a -- %.1{du:dnsmsg}\n",
11125 me->msgLen, &me->local, &me->remote, me->msgPtr, me->msgLen );
11126
11127 check( me->msgLen <= UINT16_MAX );
11128 WriteBig16( me->lenBuf, me->msgLen );
11129 me->iov[ 0 ].iov_base = me->lenBuf;
11130 me->iov[ 0 ].iov_len = sizeof( me->lenBuf );
11131 me->iov[ 1 ].iov_base = me->msgPtr;
11132 me->iov[ 1 ].iov_len = me->msgLen;
11133 me->iovPtr = me->iov;
11134 me->iovCount = 2;
11135 dispatch_resume_if_suspended( me->writeSource, &me->writeSuspended );
11136
11137 exit:
11138 FreeNullSafe( respPtr );
11139 if( err && ( err != EWOULDBLOCK ) )
11140 {
11141 _DNSServerConnectionStop( me, true );
11142 }
11143 }
11144
11145 //===========================================================================================================================
11146
11147 static void _DNSServerConnectionWriteHandler( void *inContext )
11148 {
11149 OSStatus err;
11150 const SocketContext * const sockCtx = (SocketContext *) inContext;
11151 const DNSServerConnectionRef me = (DNSServerConnectionRef) sockCtx->userContext;
11152
11153 err = SocketWriteData( sockCtx->sock, &me->iovPtr, &me->iovCount );
11154 if( !err )
11155 {
11156 me->iovPtr = NULL;
11157 me->iovCount = 0;
11158 memset( me->iov, 0, sizeof( me->iov ) );
11159 ForgetPtrLen( &me->msgPtr, &me->msgLen );
11160 dispatch_suspend_if_resumed( me->writeSource, &me->writeSuspended );
11161 dispatch_resume_if_suspended( me->readSource, &me->readSuspended );
11162 }
11163 else if( err != EWOULDBLOCK )
11164 {
11165 _DNSServerConnectionStop( me, true );
11166 }
11167 }
11168
11169 //===========================================================================================================================
11170
11171 static void _DNSServerConnectionRenewExpiration( DNSServerConnectionRef me )
11172 {
11173 me->expirationTicks = UpTicks() + SecondsToUpTicks( kDNSServerConnectionExpirationTimeSecs );
11174 }
11175
11176 //===========================================================================================================================
11177 // MDNSReplierCmd
11178 //===========================================================================================================================
11179
11180 typedef struct
11181 {
11182 uint8_t * hostname; // Used as the base name for hostnames and service names.
11183 uint8_t * serviceLabel; // Label containing the base service name.
11184 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
11185 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
11186 size_t bitmapCount; // Number of 64-bit bitmaps.
11187 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
11188 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
11189 uint32_t ifIndex; // Index of the interface to run on.
11190 unsigned int recordCountA; // Number of A records per hostname.
11191 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
11192 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
11193 double ucastDropRate; // Probability of dropping a unicast response.
11194 double mcastDropRate; // Probability of dropping a multicast query or response.
11195 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
11196 Boolean noAdditionals; // True if responses are to not include additional records.
11197 Boolean useIPv4; // True if the replier is to use IPv4.
11198 Boolean useIPv6; // True if the replier is to use IPv6.
11199 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
11200 #if( TARGET_OS_DARWIN )
11201 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
11202 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
11203 #endif
11204
11205 } MDNSReplierContext;
11206
11207 typedef struct MRResourceRecord MRResourceRecord;
11208 struct MRResourceRecord
11209 {
11210 MRResourceRecord * next; // Next item in list.
11211 uint8_t * name; // Resource record name.
11212 uint16_t type; // Resource record type.
11213 uint16_t class; // Resource record class.
11214 uint32_t ttl; // Resource record TTL.
11215 uint16_t rdlength; // Resource record data length.
11216 uint8_t * rdata; // Resource record data.
11217 const uint8_t * target; // For SRV records, pointer to target in RDATA.
11218 };
11219
11220 typedef struct MRNameOffsetItem MRNameOffsetItem;
11221 struct MRNameOffsetItem
11222 {
11223 MRNameOffsetItem * next; // Next item in list.
11224 uint16_t offset; // Offset of domain name in response message.
11225 uint8_t name[ 1 ]; // Variable-length array for domain name.
11226 };
11227
11228 #if( TARGET_OS_DARWIN )
11229 static void _MDNSReplierFollowedProcessHandler( void *inContext );
11230 #endif
11231 static void _MDNSReplierReadHandler( void *inContext );
11232 static OSStatus
11233 _MDNSReplierAnswerQuery(
11234 MDNSReplierContext * inContext,
11235 const uint8_t * inQueryPtr,
11236 size_t inQueryLen,
11237 sockaddr_ip * inSender,
11238 SocketRef inSock,
11239 unsigned int inIndex );
11240 static OSStatus
11241 _MDNSReplierAnswerListAdd(
11242 MDNSReplierContext * inContext,
11243 MRResourceRecord ** inAnswerList,
11244 unsigned int inIndex,
11245 const uint8_t * inName,
11246 unsigned int inType,
11247 unsigned int inClass );
11248 static void
11249 _MDNSReplierAnswerListRemovePTR(
11250 MRResourceRecord ** inAnswerListPtr,
11251 const uint8_t * inName,
11252 const uint8_t * inRData );
11253 static OSStatus
11254 _MDNSReplierSendOrDropResponse(
11255 MDNSReplierContext * inContext,
11256 MRResourceRecord * inAnswerList,
11257 sockaddr_ip * inQuerier,
11258 SocketRef inSock,
11259 unsigned int inIndex,
11260 Boolean inUnicast );
11261 static OSStatus
11262 _MDNSReplierCreateResponse(
11263 MDNSReplierContext * inContext,
11264 MRResourceRecord * inAnswerList,
11265 unsigned int inIndex,
11266 uint8_t ** outResponsePtr,
11267 size_t * outResponseLen );
11268 static OSStatus
11269 _MDNSReplierAppendNameToResponse(
11270 DataBuffer * inResponse,
11271 const uint8_t * inName,
11272 MRNameOffsetItem ** inNameOffsetListPtr );
11273 static Boolean
11274 _MDNSReplierServiceTypeMatch(
11275 const MDNSReplierContext * inContext,
11276 const uint8_t * inName,
11277 unsigned int * outTXTSize,
11278 unsigned int * outCount );
11279 static Boolean
11280 _MDNSReplierServiceInstanceNameMatch(
11281 const MDNSReplierContext * inContext,
11282 const uint8_t * inName,
11283 unsigned int * outIndex,
11284 unsigned int * outTXTSize,
11285 unsigned int * outCount );
11286 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
11287 static Boolean
11288 _MDNSReplierHostnameMatch(
11289 const MDNSReplierContext * inContext,
11290 const uint8_t * inName,
11291 unsigned int * outIndex );
11292 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
11293 static OSStatus
11294 _MRResourceRecordCreate(
11295 uint8_t * inName,
11296 uint16_t inType,
11297 uint16_t inClass,
11298 uint32_t inTTL,
11299 uint16_t inRDLength,
11300 uint8_t * inRData,
11301 MRResourceRecord ** outRecord );
11302 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
11303 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
11304 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
11305 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
11306 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
11307
11308 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
11309 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
11310
11311 static void MDNSReplierCmd( void )
11312 {
11313 OSStatus err;
11314 MDNSReplierContext * context;
11315 SocketRef sockV4 = kInvalidSocketRef;
11316 SocketRef sockV6 = kInvalidSocketRef;
11317 const char * ifname;
11318 size_t len;
11319 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
11320 char ifnameBuf[ IF_NAMESIZE + 1 ];
11321
11322 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
11323 require_noerr_quiet( err, exit );
11324
11325 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
11326 require_noerr_quiet( err, exit );
11327
11328 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
11329 require_noerr_quiet( err, exit );
11330
11331 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
11332 require_noerr_quiet( err, exit );
11333
11334 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
11335 require_noerr_quiet( err, exit );
11336
11337 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
11338 require_noerr_quiet( err, exit );
11339
11340 if( gMDNSReplier_Foreground )
11341 {
11342 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
11343 }
11344
11345 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
11346 require_action( context, exit, err = kNoMemoryErr );
11347
11348 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
11349 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
11350 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
11351 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
11352 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
11353 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
11354 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
11355 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
11356 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
11357 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
11358
11359 #if( TARGET_OS_DARWIN )
11360 if( gMDNSReplier_FollowPID )
11361 {
11362 context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
11363 if( err || ( context->followPID < 0 ) )
11364 {
11365 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
11366 goto exit;
11367 }
11368
11369 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
11370 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
11371 require_noerr( err, exit );
11372 dispatch_resume( context->processMonitor );
11373 }
11374 else
11375 {
11376 context->followPID = -1;
11377 }
11378 #endif
11379
11380 if( context->maxDropCount > 0 )
11381 {
11382 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
11383 require_action( context->dropCounters, exit, err = kNoMemoryErr );
11384 }
11385
11386 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
11387 require_action( context->bitmaps, exit, err = kNoMemoryErr );
11388
11389 // Create the base hostname label.
11390
11391 len = strlen( gMDNSReplier_Hostname );
11392 if( context->maxInstanceCount > 1 )
11393 {
11394 unsigned int maxInstanceCount, digitCount;
11395
11396 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
11397 // "-<instance index>" to the base hostname.
11398
11399 maxInstanceCount = context->maxInstanceCount;
11400 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
11401 len += ( 3 + digitCount );
11402 }
11403
11404 if( len <= kDomainLabelLengthMax )
11405 {
11406 uint8_t * dst = &name[ 1 ];
11407 uint8_t * lim = &name[ countof( name ) ];
11408
11409 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
11410 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
11411
11412 err = DomainNameDupLower( name, &context->hostname, NULL );
11413 require_noerr( err, exit );
11414 }
11415 else
11416 {
11417 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
11418 gMDNSReplier_Hostname, context->maxInstanceCount );
11419 goto exit;
11420 }
11421
11422 // Create the service label.
11423
11424 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
11425 if( len <= kDomainLabelLengthMax )
11426 {
11427 uint8_t * dst = &name[ 1 ];
11428 uint8_t * lim = &name[ countof( name ) ];
11429
11430 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
11431 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
11432
11433 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
11434 require_noerr( err, exit );
11435 }
11436 else
11437 {
11438 FPrintF( stderr, "error: Service type tag is too long.\n" );
11439 goto exit;
11440 }
11441
11442 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
11443 require_noerr_quiet( err, exit );
11444
11445 ifname = if_indextoname( context->ifIndex, ifnameBuf );
11446 require_action( ifname, exit, err = kNameErr );
11447
11448 // Set up IPv4 socket.
11449
11450 if( context->useIPv4 )
11451 {
11452 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
11453 require_noerr( err, exit );
11454 }
11455
11456 // Set up IPv6 socket.
11457
11458 if( context->useIPv6 )
11459 {
11460 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
11461 require_noerr( err, exit );
11462 }
11463
11464 // Create dispatch read sources for socket(s).
11465
11466 if( IsValidSocket( sockV4 ) )
11467 {
11468 SocketContext * sockCtx;
11469
11470 sockCtx = SocketContextCreate( sockV4, context, &err );
11471 require_noerr( err, exit );
11472 sockV4 = kInvalidSocketRef;
11473
11474 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
11475 &context->readSourceV4 );
11476 if( err ) ForgetSocketContext( &sockCtx );
11477 require_noerr( err, exit );
11478
11479 dispatch_resume( context->readSourceV4 );
11480 }
11481
11482 if( IsValidSocket( sockV6 ) )
11483 {
11484 SocketContext * sockCtx;
11485
11486 sockCtx = SocketContextCreate( sockV6, context, &err );
11487 require_noerr( err, exit );
11488 sockV6 = kInvalidSocketRef;
11489
11490 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
11491 &context->readSourceV6 );
11492 if( err ) ForgetSocketContext( &sockCtx );
11493 require_noerr( err, exit );
11494
11495 dispatch_resume( context->readSourceV6 );
11496 }
11497
11498 dispatch_main();
11499
11500 exit:
11501 ForgetSocket( &sockV4 );
11502 ForgetSocket( &sockV6 );
11503 exit( 1 );
11504 }
11505
11506 #if( TARGET_OS_DARWIN )
11507 //===========================================================================================================================
11508 // _MDNSReplierFollowedProcessHandler
11509 //===========================================================================================================================
11510
11511 static void _MDNSReplierFollowedProcessHandler( void *inContext )
11512 {
11513 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
11514
11515 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
11516 {
11517 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
11518 exit( 0 );
11519 }
11520 }
11521 #endif
11522
11523 //===========================================================================================================================
11524 // _MDNSReplierReadHandler
11525 //===========================================================================================================================
11526
11527 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
11528
11529 static void _MDNSReplierReadHandler( void *inContext )
11530 {
11531 OSStatus err;
11532 SocketContext * const sockCtx = (SocketContext *) inContext;
11533 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
11534 size_t msgLen;
11535 sockaddr_ip sender;
11536 const DNSHeader * hdr;
11537 unsigned int flags, questionCount, i, j;
11538 const uint8_t * ptr;
11539 int drop, isMetaQuery;
11540
11541 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
11542 NULL, NULL, NULL, NULL );
11543 require_noerr( err, exit );
11544
11545 if( msgLen < kDNSHeaderLength )
11546 {
11547 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
11548 goto exit;
11549 }
11550
11551 // Perform header field checks.
11552 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
11553
11554 hdr = (DNSHeader *) context->msgBuf;
11555 flags = DNSHeaderGetFlags( hdr );
11556 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
11557 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
11558 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
11559
11560 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
11561
11562 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s -- %#.1{du:dnsmsg}\n",
11563 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
11564
11565 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
11566
11567 questionCount = DNSHeaderGetQuestionCount( hdr );
11568 require_quiet( questionCount > 0, exit );
11569
11570 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
11571
11572 isMetaQuery = false;
11573 ptr = (const uint8_t *) &hdr[ 1 ];
11574 for( i = 0; i < questionCount; ++i )
11575 {
11576 unsigned int count, index;
11577 uint16_t qtype, qclass;
11578 uint8_t qname[ kDomainNameLengthMax ];
11579
11580 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
11581 require_noerr_quiet( err, exit );
11582
11583 if( ( qclass & ~kMDNSClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
11584
11585 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
11586 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
11587 {
11588 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
11589 {
11590 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
11591 }
11592 }
11593 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
11594 {
11595 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
11596 {
11597 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
11598 {
11599 if( count < 64 )
11600 {
11601 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
11602 break;
11603 }
11604 else
11605 {
11606 context->bitmaps[ j ] = ~UINT64_C( 0 );
11607 count -= 64;
11608 }
11609 }
11610 }
11611 }
11612 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
11613 {
11614 isMetaQuery = true;
11615 }
11616 }
11617
11618 // Attempt to answer the query message using selected record sets.
11619
11620 if( isMetaQuery )
11621 {
11622 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
11623 check_noerr( err );
11624 }
11625 if( drop ) goto exit;
11626
11627 for( i = 0; i < context->bitmapCount; ++i )
11628 {
11629 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
11630 {
11631 const uint64_t bitmask = UINT64_C( 1 ) << j;
11632
11633 if( context->bitmaps[ i ] & bitmask )
11634 {
11635 context->bitmaps[ i ] &= ~bitmask;
11636
11637 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
11638 ( i * 64 ) + j + 1 );
11639 check_noerr( err );
11640 }
11641 }
11642 }
11643
11644 exit:
11645 return;
11646 }
11647
11648 //===========================================================================================================================
11649 // _MDNSReplierAnswerQuery
11650 //===========================================================================================================================
11651
11652 static OSStatus
11653 _MDNSReplierAnswerQuery(
11654 MDNSReplierContext * inContext,
11655 const uint8_t * inQueryPtr,
11656 size_t inQueryLen,
11657 sockaddr_ip * inSender,
11658 SocketRef inSock,
11659 unsigned int inIndex )
11660 {
11661 OSStatus err;
11662 const DNSHeader * hdr;
11663 const uint8_t * ptr;
11664 unsigned int questionCount, answerCount, i;
11665 MRResourceRecord * ucastAnswerList = NULL;
11666 MRResourceRecord * mcastAnswerList = NULL;
11667
11668 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
11669
11670 // Get answers for questions.
11671
11672 check( inQueryLen >= kDNSHeaderLength );
11673 hdr = (const DNSHeader *) inQueryPtr;
11674 questionCount = DNSHeaderGetQuestionCount( hdr );
11675
11676 ptr = (const uint8_t *) &hdr[ 1 ];
11677 for( i = 0; i < questionCount; ++i )
11678 {
11679 MRResourceRecord ** answerListPtr;
11680 uint16_t qtype, qclass;
11681 uint8_t qname[ kDomainNameLengthMax ];
11682
11683 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
11684 require_noerr_quiet( err, exit );
11685
11686 if( qclass & kMDNSClassUnicastResponseBit )
11687 {
11688 qclass &= ~kMDNSClassUnicastResponseBit;
11689 answerListPtr = &ucastAnswerList;
11690 }
11691 else
11692 {
11693 answerListPtr = &mcastAnswerList;
11694 }
11695
11696 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
11697 require_noerr( err, exit );
11698 }
11699 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
11700
11701 // Suppress known answers.
11702 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
11703 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
11704
11705 answerCount = DNSHeaderGetAnswerCount( hdr );
11706 for( i = 0; i < answerCount; ++i )
11707 {
11708 const uint8_t * rdataPtr;
11709 const uint8_t * recordPtr;
11710 uint16_t type, class;
11711 uint8_t name[ kDomainNameLengthMax ];
11712 uint8_t instance[ kDomainNameLengthMax ];
11713
11714 recordPtr = ptr;
11715 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
11716 require_noerr_quiet( err, exit );
11717
11718 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
11719
11720 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
11721 require_noerr( err, exit );
11722
11723 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
11724 require_noerr_quiet( err, exit );
11725
11726 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
11727 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
11728 }
11729 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
11730
11731 // Send or drop responses.
11732
11733 if( ucastAnswerList )
11734 {
11735 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
11736 require_noerr( err, exit );
11737 }
11738
11739 if( mcastAnswerList )
11740 {
11741 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
11742 require_noerr( err, exit );
11743 }
11744 err = kNoErr;
11745
11746 exit:
11747 _MRResourceRecordFreeList( ucastAnswerList );
11748 _MRResourceRecordFreeList( mcastAnswerList );
11749 return( err );
11750 }
11751
11752 //===========================================================================================================================
11753 // _MDNSReplierAnswerListAdd
11754 //===========================================================================================================================
11755
11756 static OSStatus
11757 _MDNSReplierAnswerListAdd(
11758 MDNSReplierContext * inContext,
11759 MRResourceRecord ** inAnswerList,
11760 unsigned int inIndex,
11761 const uint8_t * inName,
11762 unsigned int inType,
11763 unsigned int inClass )
11764 {
11765 OSStatus err;
11766 uint8_t * recordName = NULL;
11767 uint8_t * rdataPtr = NULL;
11768 size_t rdataLen;
11769 MRResourceRecord * answer;
11770 MRResourceRecord ** answerPtr;
11771 const uint8_t * const hostname = inContext->hostname;
11772 unsigned int i;
11773 uint32_t index;
11774 unsigned int count, txtSize;
11775
11776 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
11777 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
11778
11779 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
11780 {
11781 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
11782 {
11783 err = kNoErr;
11784 goto exit;
11785 }
11786 }
11787
11788 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
11789 // instance count are for answering queries about service instances.
11790
11791 if( inIndex == 0 )
11792 {
11793 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
11794 {
11795 int listHasTXT = false;
11796
11797 if( inType == kDNSServiceType_ANY )
11798 {
11799 for( answer = *inAnswerList; answer; answer = answer->next )
11800 {
11801 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
11802 {
11803 listHasTXT = true;
11804 break;
11805 }
11806 }
11807 }
11808
11809 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
11810 {
11811 err = DomainNameDupLower( inName, &recordName, NULL );
11812 require_noerr( err, exit );
11813
11814 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
11815 require_noerr( err, exit );
11816
11817 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
11818 (uint16_t) rdataLen, rdataPtr, &answer );
11819 require_noerr( err, exit );
11820 recordName = NULL;
11821 rdataPtr = NULL;
11822
11823 *answerPtr = answer;
11824 }
11825 else if( inType == kDNSServiceType_NSEC )
11826 {
11827 err = DomainNameDupLower( inName, &recordName, NULL );
11828 require_noerr( err, exit );
11829
11830 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
11831 require_noerr( err, exit );
11832
11833 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11834 (uint16_t) rdataLen, rdataPtr, &answer );
11835 require_noerr( err, exit );
11836 recordName = NULL;
11837 rdataPtr = NULL;
11838
11839 *answerPtr = answer;
11840 }
11841 }
11842 }
11843 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
11844 {
11845 int listHasA = false;
11846 int listHasAAAA = false;
11847
11848 if( inType == kDNSServiceType_ANY )
11849 {
11850 for( answer = *inAnswerList; answer; answer = answer->next )
11851 {
11852 if( answer->type == kDNSServiceType_A )
11853 {
11854 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
11855 }
11856 else if( answer->type == kDNSServiceType_AAAA )
11857 {
11858 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
11859 }
11860 if( listHasA && listHasAAAA ) break;
11861 }
11862 }
11863
11864 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
11865 {
11866 for( i = 1; i <= inContext->recordCountA; ++i )
11867 {
11868 err = DomainNameDupLower( inName, &recordName, NULL );
11869 require_noerr( err, exit );
11870
11871 rdataLen = 4;
11872 rdataPtr = (uint8_t *) malloc( rdataLen );
11873 require_action( rdataPtr, exit, err = kNoMemoryErr );
11874
11875 rdataPtr[ 0 ] = 0;
11876 WriteBig16( &rdataPtr[ 1 ], inIndex );
11877 rdataPtr[ 3 ] = (uint8_t) i;
11878
11879 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11880 (uint16_t) rdataLen, rdataPtr, &answer );
11881 require_noerr( err, exit );
11882 recordName = NULL;
11883 rdataPtr = NULL;
11884
11885 *answerPtr = answer;
11886 answerPtr = &answer->next;
11887 }
11888 }
11889
11890 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
11891 {
11892 for( i = 1; i <= inContext->recordCountAAAA; ++i )
11893 {
11894 const uint8_t ( *baseAddr )[ 16 ];
11895
11896 err = DomainNameDupLower( inName, &recordName, NULL );
11897 require_noerr( err, exit );
11898
11899 rdataLen = 16;
11900 baseAddr = ( i == 1 ) ? &kMDNSReplierLinkLocalBaseAddrV6 : &kMDNSReplierBaseAddrV6;
11901 rdataPtr = (uint8_t *) _memdup( baseAddr, rdataLen );
11902 require_action( rdataPtr, exit, err = kNoMemoryErr );
11903
11904 WriteBig16( &rdataPtr[ 12 ], inIndex );
11905 rdataPtr[ 15 ] = (uint8_t) i;
11906
11907 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11908 (uint16_t) rdataLen, rdataPtr, &answer );
11909 require_noerr( err, exit );
11910 recordName = NULL;
11911 rdataPtr = NULL;
11912
11913 *answerPtr = answer;
11914 answerPtr = &answer->next;
11915 }
11916 }
11917 else if( inType == kDNSServiceType_NSEC )
11918 {
11919 err = DomainNameDupLower( inName, &recordName, NULL );
11920 require_noerr( err, exit );
11921
11922 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
11923 {
11924 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
11925 require_noerr( err, exit );
11926 }
11927 else if( inContext->recordCountA > 0 )
11928 {
11929 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
11930 require_noerr( err, exit );
11931 }
11932 else if( inContext->recordCountAAAA > 0 )
11933 {
11934 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
11935 require_noerr( err, exit );
11936 }
11937 else
11938 {
11939 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
11940 require_noerr( err, exit );
11941 }
11942
11943 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11944 (uint16_t) rdataLen, rdataPtr, &answer );
11945 require_noerr( err, exit );
11946 recordName = NULL;
11947 rdataPtr = NULL;
11948
11949 *answerPtr = answer;
11950 }
11951 }
11952 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
11953 {
11954 int listHasPTR = false;
11955
11956 if( inType == kDNSServiceType_ANY )
11957 {
11958 for( answer = *inAnswerList; answer; answer = answer->next )
11959 {
11960 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
11961 {
11962 listHasPTR = true;
11963 break;
11964 }
11965 }
11966 }
11967
11968 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
11969 {
11970 size_t recordNameLen;
11971 uint8_t * ptr;
11972 uint8_t * lim;
11973
11974 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
11975 require_noerr( err, exit );
11976
11977 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
11978 rdataPtr = (uint8_t *) malloc( rdataLen );
11979 require_action( rdataPtr, exit, err = kNoMemoryErr );
11980
11981 lim = &rdataPtr[ rdataLen ];
11982
11983 ptr = &rdataPtr[ 1 ];
11984 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
11985 ptr += hostname[ 0 ];
11986 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
11987 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
11988
11989 check( (size_t)( lim - ptr ) >= recordNameLen );
11990 memcpy( ptr, recordName, recordNameLen );
11991 ptr += recordNameLen;
11992
11993 rdataLen = (size_t)( ptr - rdataPtr );
11994
11995 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
11996 (uint16_t) rdataLen, rdataPtr, &answer );
11997 require_noerr( err, exit );
11998 recordName = NULL;
11999 rdataPtr = NULL;
12000
12001 *answerPtr = answer;
12002 }
12003 }
12004 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
12005 ( index == inIndex ) && ( count >= inIndex ) )
12006 {
12007 int listHasSRV = false;
12008 int listHasTXT = false;
12009
12010 if( inType == kDNSServiceType_ANY )
12011 {
12012 for( answer = *inAnswerList; answer; answer = answer->next )
12013 {
12014 if( answer->type == kDNSServiceType_SRV )
12015 {
12016 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
12017 }
12018 else if( answer->type == kDNSServiceType_TXT )
12019 {
12020 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
12021 }
12022 if( listHasSRV && listHasTXT ) break;
12023 }
12024 }
12025
12026 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
12027 {
12028 dns_fixed_fields_srv * fields;
12029 uint8_t * ptr;
12030 uint8_t * lim;
12031 uint8_t * targetPtr;
12032
12033 err = DomainNameDupLower( inName, &recordName, NULL );
12034 require_noerr( err, exit );
12035
12036 rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
12037 rdataPtr = (uint8_t *) malloc( rdataLen );
12038 require_action( rdataPtr, exit, err = kNoMemoryErr );
12039
12040 lim = &rdataPtr[ rdataLen ];
12041
12042 fields = (dns_fixed_fields_srv *) rdataPtr;
12043 dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
12044
12045 targetPtr = (uint8_t *) &fields[ 1 ];
12046
12047 ptr = &targetPtr[ 1 ];
12048 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
12049 ptr += hostname[ 0 ];
12050 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
12051 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
12052
12053 check( (size_t)( lim - ptr ) >= kLocalNameLen );
12054 memcpy( ptr, kLocalName, kLocalNameLen );
12055 ptr += kLocalNameLen;
12056
12057 rdataLen = (size_t)( ptr - rdataPtr );
12058
12059 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
12060 (uint16_t) rdataLen, rdataPtr, &answer );
12061 require_noerr( err, exit );
12062 recordName = NULL;
12063 rdataPtr = NULL;
12064
12065 *answerPtr = answer;
12066 answerPtr = &answer->next;
12067 }
12068
12069 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
12070 {
12071 err = DomainNameDupLower( inName, &recordName, NULL );
12072 require_noerr( err, exit );
12073
12074 rdataLen = txtSize;
12075 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
12076 require_noerr( err, exit );
12077
12078 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
12079 (uint16_t) rdataLen, rdataPtr, &answer );
12080 require_noerr( err, exit );
12081 recordName = NULL;
12082 rdataPtr = NULL;
12083
12084 *answerPtr = answer;
12085 }
12086 else if( inType == kDNSServiceType_NSEC )
12087 {
12088 err = DomainNameDupLower( inName, &recordName, NULL );
12089 require_noerr( err, exit );
12090
12091 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
12092 require_noerr( err, exit );
12093
12094 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
12095 (uint16_t) rdataLen, rdataPtr, &answer );
12096 require_noerr( err, exit );
12097 recordName = NULL;
12098 rdataPtr = NULL;
12099
12100 *answerPtr = answer;
12101 }
12102 }
12103 err = kNoErr;
12104
12105 exit:
12106 FreeNullSafe( recordName );
12107 FreeNullSafe( rdataPtr );
12108 return( err );
12109 }
12110
12111 //===========================================================================================================================
12112 // _MDNSReplierAnswerListRemovePTR
12113 //===========================================================================================================================
12114
12115 static void
12116 _MDNSReplierAnswerListRemovePTR(
12117 MRResourceRecord ** inAnswerListPtr,
12118 const uint8_t * inName,
12119 const uint8_t * inRData )
12120 {
12121 MRResourceRecord * answer;
12122 MRResourceRecord ** answerPtr;
12123
12124 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
12125 {
12126 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
12127 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
12128 }
12129 if( answer )
12130 {
12131 *answerPtr = answer->next;
12132 _MRResourceRecordFree( answer );
12133 }
12134 }
12135
12136 //===========================================================================================================================
12137 // _MDNSReplierSendOrDropResponse
12138 //===========================================================================================================================
12139
12140 static OSStatus
12141 _MDNSReplierSendOrDropResponse(
12142 MDNSReplierContext * inContext,
12143 MRResourceRecord * inAnswerList,
12144 sockaddr_ip * inQuerier,
12145 SocketRef inSock,
12146 unsigned int inIndex,
12147 Boolean inUnicast )
12148 {
12149 OSStatus err;
12150 uint8_t * responsePtr = NULL;
12151 size_t responseLen;
12152 const struct sockaddr * destAddr;
12153 ssize_t n;
12154 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
12155 int drop;
12156
12157 check( inIndex <= inContext->maxInstanceCount );
12158
12159 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
12160 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
12161 // as opposed to those for service instance records, are never dropped.
12162
12163 drop = false;
12164 if( inIndex > 0 )
12165 {
12166 if( inContext->maxDropCount > 0 )
12167 {
12168 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
12169
12170 if( *dropCount < inContext->maxDropCount )
12171 {
12172 if( ShouldDrop( dropRate ) ) drop = true;
12173 *dropCount += 1;
12174 }
12175 }
12176 else if( ShouldDrop( dropRate ) )
12177 {
12178 drop = true;
12179 }
12180 }
12181
12182 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
12183 require_noerr( err, exit );
12184
12185 if( inUnicast )
12186 {
12187 destAddr = &inQuerier->sa;
12188 }
12189 else
12190 {
12191 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
12192 }
12193
12194 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a -- %#.1{du:dnsmsg}\n",
12195 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
12196
12197 if( !drop )
12198 {
12199 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
12200 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
12201 require_noerr( err, exit );
12202 }
12203
12204 exit:
12205 FreeNullSafe( responsePtr );
12206 return( err );
12207 }
12208
12209 //===========================================================================================================================
12210 // _MDNSReplierCreateResponse
12211 //===========================================================================================================================
12212
12213 static OSStatus
12214 _MDNSReplierCreateResponse(
12215 MDNSReplierContext * inContext,
12216 MRResourceRecord * inAnswerList,
12217 unsigned int inIndex,
12218 uint8_t ** outResponsePtr,
12219 size_t * outResponseLen )
12220 {
12221 OSStatus err;
12222 DataBuffer responseDB;
12223 DNSHeader hdr;
12224 MRResourceRecord * answer;
12225 uint8_t * responsePtr;
12226 size_t responseLen, len;
12227 unsigned int answerCount, recordCount;
12228 MRNameOffsetItem * nameOffsetList = NULL;
12229
12230 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
12231
12232 // The current answers in the answer list will make up the response's Answer Record Section.
12233
12234 answerCount = 0;
12235 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
12236
12237 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
12238
12239 if( !inContext->noAdditionals )
12240 {
12241 for( answer = inAnswerList; answer; answer = answer->next )
12242 {
12243 switch( answer->type )
12244 {
12245 case kDNSServiceType_PTR:
12246 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
12247 answer->class );
12248 require_noerr( err, exit );
12249
12250 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
12251 answer->class );
12252 require_noerr( err, exit );
12253 break;
12254
12255 case kDNSServiceType_SRV:
12256 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
12257 answer->class );
12258 require_noerr( err, exit );
12259
12260 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
12261 answer->class );
12262 require_noerr( err, exit );
12263
12264 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12265 answer->class );
12266 require_noerr( err, exit );
12267 break;
12268
12269 case kDNSServiceType_TXT:
12270 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12271 answer->class );
12272 require_noerr( err, exit );
12273 break;
12274
12275 case kDNSServiceType_A:
12276 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
12277 answer->class );
12278 require_noerr( err, exit );
12279
12280 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12281 answer->class );
12282 require_noerr( err, exit );
12283 break;
12284
12285 case kDNSServiceType_AAAA:
12286 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
12287 answer->class );
12288 require_noerr( err, exit );
12289
12290 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12291 answer->class );
12292 require_noerr( err, exit );
12293 break;
12294
12295 default:
12296 break;
12297 }
12298 }
12299 }
12300
12301 // Append a provisional header to the response message.
12302
12303 memset( &hdr, 0, sizeof( hdr ) );
12304 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
12305
12306 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
12307 require_noerr( err, exit );
12308
12309 // Append answers to response message.
12310
12311 responseLen = DataBuffer_GetLen( &responseDB );
12312 recordCount = 0;
12313 for( answer = inAnswerList; answer; answer = answer->next )
12314 {
12315 dns_fixed_fields_record fields;
12316 unsigned int class;
12317
12318 // Append record NAME.
12319
12320 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
12321 require_noerr( err, exit );
12322
12323 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
12324
12325 class = answer->class;
12326 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
12327 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
12328 ( answer->type == kDNSServiceType_NSEC ) )
12329 {
12330 class |= kMDNSClassCacheFlushBit;
12331 }
12332
12333 dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
12334 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
12335 require_noerr( err, exit );
12336
12337 // Append record RDATA.
12338 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
12339
12340 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
12341 ( answer->type == kDNSServiceType_NSEC ) )
12342 {
12343 size_t rdlength;
12344 uint8_t * rdLengthPtr;
12345 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
12346 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
12347
12348 if( answer->type == kDNSServiceType_PTR )
12349 {
12350 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
12351 require_noerr( err, exit );
12352 }
12353 else if( answer->type == kDNSServiceType_SRV )
12354 {
12355 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
12356
12357 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
12358 require_noerr( err, exit );
12359
12360 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
12361 require_noerr( err, exit );
12362 }
12363 else
12364 {
12365 const size_t nameLen = DomainNameLength( answer->rdata );
12366
12367 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
12368 require_noerr( err, exit );
12369
12370 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
12371
12372 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
12373 require_noerr( err, exit );
12374 }
12375
12376 // Set the actual RDLENGTH, which may be less than the original due to name compression.
12377
12378 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
12379 check( rdlength <= UINT16_MAX );
12380
12381 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
12382 WriteBig16( rdLengthPtr, rdlength );
12383 }
12384 else
12385 {
12386 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
12387 require_noerr( err, exit );
12388 }
12389
12390 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
12391 responseLen = DataBuffer_GetLen( &responseDB );
12392 ++recordCount;
12393 }
12394
12395 // Set the response header's Answer and Additional record counts.
12396 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
12397 // response message to exceed the maximum mDNS message size.
12398
12399 if( recordCount <= answerCount )
12400 {
12401 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
12402 }
12403 else
12404 {
12405 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
12406 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
12407 }
12408
12409 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
12410 require_noerr( err, exit );
12411
12412 if( outResponsePtr ) *outResponsePtr = responsePtr;
12413 if( outResponseLen ) *outResponseLen = responseLen;
12414
12415 exit:
12416 _MRNameOffsetItemFreeList( nameOffsetList );
12417 DataBuffer_Free( &responseDB );
12418 return( err );
12419 }
12420
12421 //===========================================================================================================================
12422 // _MDNSReplierAppendNameToResponse
12423 //===========================================================================================================================
12424
12425 static OSStatus
12426 _MDNSReplierAppendNameToResponse(
12427 DataBuffer * inResponse,
12428 const uint8_t * inName,
12429 MRNameOffsetItem ** inNameOffsetListPtr )
12430 {
12431 OSStatus err;
12432 const uint8_t * subname;
12433 const uint8_t * limit;
12434 size_t nameOffset;
12435 MRNameOffsetItem * item;
12436 uint8_t compressionPtr[ 2 ];
12437
12438 nameOffset = DataBuffer_GetLen( inResponse );
12439
12440 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
12441
12442 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
12443 {
12444 for( item = *inNameOffsetListPtr; item; item = item->next )
12445 {
12446 if( DomainNameEqual( item->name, subname ) ) break;
12447 }
12448
12449 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
12450 // the subname's first label.
12451
12452 if( item )
12453 {
12454 DNSMessageWriteLabelPointer( compressionPtr, item->offset );
12455
12456 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
12457 require_noerr( err, exit );
12458 break;
12459 }
12460 else
12461 {
12462 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
12463 require_noerr( err, exit );
12464 }
12465 }
12466
12467 // If we made it to the root label, then no subname was able to be compressed. All of the name's labels up to the root
12468 // label were appended to the response message, so a root label is needed to terminate the complete name.
12469
12470 if( subname[ 0 ] == 0 )
12471 {
12472 err = DataBuffer_Append( inResponse, "", 1 );
12473 require_noerr( err, exit );
12474 }
12475
12476 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
12477
12478 limit = subname;
12479 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
12480 {
12481 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
12482
12483 if( subnameOffset > kDNSCompressionOffsetMax ) break;
12484
12485 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
12486 require_noerr( err, exit );
12487
12488 item->next = *inNameOffsetListPtr;
12489 *inNameOffsetListPtr = item;
12490 }
12491 err = kNoErr;
12492
12493 exit:
12494 return( err );
12495 }
12496
12497 //===========================================================================================================================
12498 // _MDNSReplierServiceTypeMatch
12499 //===========================================================================================================================
12500
12501 static Boolean
12502 _MDNSReplierServiceTypeMatch(
12503 const MDNSReplierContext * inContext,
12504 const uint8_t * inName,
12505 unsigned int * outTXTSize,
12506 unsigned int * outCount )
12507 {
12508 OSStatus err;
12509 const char * ptr;
12510 const char * end;
12511 uint32_t txtSize, count;
12512 const uint8_t * const serviceLabel = inContext->serviceLabel;
12513 int nameMatches = false;
12514
12515 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
12516 if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
12517
12518 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
12519 end = (const char *) &inName[ 1 + inName[ 0 ] ];
12520
12521 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
12522 ++ptr;
12523
12524 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
12525 require_noerr_quiet( err, exit );
12526 require_quiet( txtSize <= UINT16_MAX, exit );
12527
12528 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
12529 ++ptr;
12530
12531 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
12532 require_noerr_quiet( err, exit );
12533 require_quiet( count <= UINT16_MAX, exit );
12534 require_quiet( ptr == end, exit );
12535
12536 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
12537 nameMatches = true;
12538
12539 if( outTXTSize ) *outTXTSize = txtSize;
12540 if( outCount ) *outCount = count;
12541
12542 exit:
12543 return( nameMatches ? true : false );
12544 }
12545
12546 //===========================================================================================================================
12547 // _MDNSReplierServiceInstanceNameMatch
12548 //===========================================================================================================================
12549
12550 static Boolean
12551 _MDNSReplierServiceInstanceNameMatch(
12552 const MDNSReplierContext * inContext,
12553 const uint8_t * inName,
12554 unsigned int * outIndex,
12555 unsigned int * outTXTSize,
12556 unsigned int * outCount )
12557 {
12558 OSStatus err;
12559 const uint8_t * ptr;
12560 const uint8_t * end;
12561 uint32_t index;
12562 unsigned int txtSize, count;
12563 const uint8_t * const hostname = inContext->hostname;
12564 int nameMatches = false;
12565
12566 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
12567 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
12568
12569 ptr = &inName[ 1 + hostname[ 0 ] ];
12570 end = &inName[ 1 + inName[ 0 ] ];
12571 if( ptr < end )
12572 {
12573 require_quiet( ( end - ptr ) >= 2, exit );
12574 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
12575 ptr += 2;
12576
12577 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
12578 require_noerr_quiet( err, exit );
12579 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
12580
12581 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
12582 ++ptr;
12583 }
12584 else
12585 {
12586 index = 1;
12587 }
12588
12589 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
12590 nameMatches = true;
12591
12592 if( outIndex ) *outIndex = index;
12593 if( outTXTSize ) *outTXTSize = txtSize;
12594 if( outCount ) *outCount = count;
12595
12596 exit:
12597 return( nameMatches ? true : false );
12598 }
12599
12600 //===========================================================================================================================
12601 // _MDNSReplierAboutRecordNameMatch
12602 //===========================================================================================================================
12603
12604 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
12605 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
12606
12607 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
12608 {
12609 const uint8_t * subname;
12610 const uint8_t * const hostname = inContext->hostname;
12611 int nameMatches = false;
12612
12613 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
12614 subname = DomainNameGetNextLabel( inName );
12615
12616 if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
12617 subname = DomainNameGetNextLabel( subname );
12618
12619 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
12620 nameMatches = true;
12621
12622 exit:
12623 return( nameMatches ? true : false );
12624 }
12625
12626 //===========================================================================================================================
12627 // _MDNSReplierHostnameMatch
12628 //===========================================================================================================================
12629
12630 static Boolean
12631 _MDNSReplierHostnameMatch(
12632 const MDNSReplierContext * inContext,
12633 const uint8_t * inName,
12634 unsigned int * outIndex )
12635 {
12636 OSStatus err;
12637 const uint8_t * ptr;
12638 const uint8_t * end;
12639 uint32_t index;
12640 const uint8_t * const hostname = inContext->hostname;
12641 int nameMatches = false;
12642
12643 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
12644 if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
12645
12646 ptr = &inName[ 1 + hostname[ 0 ] ];
12647 end = &inName[ 1 + inName[ 0 ] ];
12648 if( ptr < end )
12649 {
12650 require_quiet( *ptr == '-', exit );
12651 ++ptr;
12652
12653 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
12654 require_noerr_quiet( err, exit );
12655 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
12656 require_quiet( ptr == end, exit );
12657 }
12658 else
12659 {
12660 index = 1;
12661 }
12662
12663 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
12664 nameMatches = true;
12665
12666 if( outIndex ) *outIndex = index;
12667
12668 exit:
12669 return( nameMatches ? true : false );
12670 }
12671
12672 //===========================================================================================================================
12673 // _MDNSReplierCreateTXTRecord
12674 //===========================================================================================================================
12675
12676 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
12677 {
12678 OSStatus err;
12679 uint8_t * txt;
12680 uint8_t * ptr;
12681 size_t i, wholeCount, remCount;
12682 uint32_t hash;
12683 int n;
12684 uint8_t txtStr[ 16 ];
12685
12686 require_action_quiet( inSize > 0, exit, err = kSizeErr );
12687
12688 txt = (uint8_t *) malloc( inSize );
12689 require_action( txt, exit, err = kNoMemoryErr );
12690
12691 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
12692
12693 txtStr[ 0 ] = 15;
12694 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12695 check( n == 15 );
12696
12697 ptr = txt;
12698 wholeCount = inSize / 16;
12699 for( i = 0; i < wholeCount; ++i )
12700 {
12701 memcpy( ptr, txtStr, 16 );
12702 ptr += 16;
12703 }
12704
12705 remCount = inSize % 16;
12706 if( remCount > 0 )
12707 {
12708 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12709 memcpy( ptr, txtStr, remCount );
12710 ptr += remCount;
12711 }
12712 check( ptr == &txt[ inSize ] );
12713
12714 *outTXT = txt;
12715 err = kNoErr;
12716
12717 exit:
12718 return( err );
12719 }
12720
12721 //===========================================================================================================================
12722 // _MRResourceRecordCreate
12723 //===========================================================================================================================
12724
12725 static OSStatus
12726 _MRResourceRecordCreate(
12727 uint8_t * inName,
12728 uint16_t inType,
12729 uint16_t inClass,
12730 uint32_t inTTL,
12731 uint16_t inRDLength,
12732 uint8_t * inRData,
12733 MRResourceRecord ** outRecord )
12734 {
12735 OSStatus err;
12736 MRResourceRecord * obj;
12737
12738 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
12739 require_action( obj, exit, err = kNoMemoryErr );
12740
12741 obj->name = inName;
12742 obj->type = inType;
12743 obj->class = inClass;
12744 obj->ttl = inTTL;
12745 obj->rdlength = inRDLength;
12746 obj->rdata = inRData;
12747
12748 if( inType == kDNSServiceType_SRV )
12749 {
12750 require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
12751 obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
12752 }
12753
12754 *outRecord = obj;
12755 obj = NULL;
12756 err = kNoErr;
12757
12758 exit:
12759 FreeNullSafe( obj );
12760 return( err );
12761 }
12762
12763 //===========================================================================================================================
12764 // _MRResourceRecordFree
12765 //===========================================================================================================================
12766
12767 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
12768 {
12769 ForgetMem( &inRecord->name );
12770 ForgetMem( &inRecord->rdata );
12771 free( inRecord );
12772 }
12773
12774 //===========================================================================================================================
12775 // _MRResourceRecordFreeList
12776 //===========================================================================================================================
12777
12778 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
12779 {
12780 MRResourceRecord * record;
12781
12782 while( ( record = inList ) != NULL )
12783 {
12784 inList = record->next;
12785 _MRResourceRecordFree( record );
12786 }
12787 }
12788
12789 //===========================================================================================================================
12790 // _MRNameOffsetItemCreate
12791 //===========================================================================================================================
12792
12793 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
12794 {
12795 OSStatus err;
12796 MRNameOffsetItem * obj;
12797 size_t nameLen;
12798
12799 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
12800
12801 nameLen = DomainNameLength( inName );
12802 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
12803 require_action( obj, exit, err = kNoMemoryErr );
12804
12805 obj->offset = inOffset;
12806 memcpy( obj->name, inName, nameLen );
12807
12808 *outItem = obj;
12809 err = kNoErr;
12810
12811 exit:
12812 return( err );
12813 }
12814
12815 //===========================================================================================================================
12816 // _MRNameOffsetItemFree
12817 //===========================================================================================================================
12818
12819 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
12820 {
12821 free( inItem );
12822 }
12823
12824 //===========================================================================================================================
12825 // _MRNameOffsetItemFreeList
12826 //===========================================================================================================================
12827
12828 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
12829 {
12830 MRNameOffsetItem * item;
12831
12832 while( ( item = inList ) != NULL )
12833 {
12834 inList = item->next;
12835 _MRNameOffsetItemFree( item );
12836 }
12837 }
12838
12839 //===========================================================================================================================
12840 // GAIPerfCmd
12841 //===========================================================================================================================
12842
12843 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
12844
12845 typedef struct GAITesterPrivate * GAITesterRef;
12846 typedef struct GAITestCase GAITestCase;
12847
12848 typedef struct
12849 {
12850 const char * name; // Domain name that was resolved.
12851 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
12852 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
12853 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
12854 OSStatus error;
12855
12856 } GAITestItemResult;
12857
12858 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
12859 typedef void
12860 ( *GAITesterResultsHandler_f )(
12861 const char * inCaseTitle,
12862 NanoTime64 inCaseStartTime,
12863 NanoTime64 inCaseEndTime,
12864 const GAITestItemResult * inResultArray,
12865 size_t inResultCount,
12866 void * inContext );
12867
12868 typedef unsigned int GAITestAddrType;
12869 #define kGAITestAddrType_None 0
12870 #define kGAITestAddrType_IPv4 ( 1U << 0 )
12871 #define kGAITestAddrType_IPv6 ( 1U << 1 )
12872 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
12873
12874 #define GAITestAddrTypeIsValid( X ) \
12875 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
12876
12877 typedef struct
12878 {
12879 GAITesterRef tester; // GAI tester object.
12880 CFMutableArrayRef testCaseResults; // Array of test case results.
12881 unsigned int iterTimeLimitMs; // Amount of time to allow each iteration to complete.
12882 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
12883 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
12884 unsigned int defaultIterCount; // Default test case iteration count.
12885 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
12886 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
12887 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12888 OutputFormatType outputFormat; // Format of test results output.
12889 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
12890 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
12891 Boolean testFailed; // True if at least one test case iteration failed.
12892
12893 } GAIPerfContext;
12894
12895 static void GAIPerfContextFree( GAIPerfContext *inContext );
12896 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
12897 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
12898 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
12899 static void
12900 GAIPerfResultsHandler(
12901 const char * inCaseTitle,
12902 NanoTime64 inCaseStartTime,
12903 NanoTime64 inCaseEndTime,
12904 const GAITestItemResult * inResultArray,
12905 size_t inResultCount,
12906 void * inContext );
12907 static void GAIPerfSignalHandler( void *inContext );
12908
12909 static CFTypeID GAITesterGetTypeID( void );
12910 static OSStatus
12911 GAITesterCreate(
12912 dispatch_queue_t inQueue,
12913 unsigned int inCallDelayMs,
12914 int inServerDelayMs,
12915 int inServerDefaultTTL,
12916 Boolean inSkipPathEvaluation,
12917 Boolean inBadUDPMode,
12918 GAITesterRef * outTester );
12919 static void GAITesterStart( GAITesterRef inTester );
12920 static void GAITesterStop( GAITesterRef inTester );
12921 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
12922 static void
12923 GAITesterSetStopHandler(
12924 GAITesterRef inTester,
12925 GAITesterStopHandler_f inEventHandler,
12926 void * inEventContext );
12927 static void
12928 GAITesterSetResultsHandler(
12929 GAITesterRef inTester,
12930 GAITesterResultsHandler_f inResultsHandler,
12931 void * inResultsContext );
12932
12933 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
12934 static void GAITestCaseFree( GAITestCase *inCase );
12935 static OSStatus
12936 GAITestCaseAddItem(
12937 GAITestCase * inCase,
12938 unsigned int inAliasCount,
12939 unsigned int inAddressCount,
12940 int inTTL,
12941 GAITestAddrType inHasAddrs,
12942 GAITestAddrType inWantAddrs,
12943 unsigned int inTimeLimitMs,
12944 unsigned int inItemCount );
12945 static OSStatus
12946 GAITestCaseAddLocalHostItem(
12947 GAITestCase * inCase,
12948 GAITestAddrType inWantAddrs,
12949 unsigned int inTimeLimitMs,
12950 unsigned int inItemCount );
12951
12952 static void GAIPerfCmd( void )
12953 {
12954 OSStatus err;
12955 GAIPerfContext * context = NULL;
12956
12957 err = CheckRootUser();
12958 require_noerr_quiet( err, exit );
12959
12960 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
12961 require_noerr_quiet( err, exit );
12962
12963 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
12964 require_noerr_quiet( err, exit );
12965
12966 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
12967 require_noerr_quiet( err, exit );
12968
12969 err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
12970 require_noerr_quiet( err, exit );
12971
12972 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
12973 require_action( context, exit, err = kNoMemoryErr );
12974
12975 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12976 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
12977
12978 context->iterTimeLimitMs = (unsigned int) gGAIPerf_IterationTimeLimitMs;
12979 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
12980 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
12981 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
12982 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
12983 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
12984
12985 if( gGAIPerf_OutputFilePath )
12986 {
12987 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
12988 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
12989 }
12990
12991 err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
12992 require_noerr_quiet( err, exit );
12993
12994 err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
12995 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
12996 require_noerr( err, exit );
12997
12998 check( gGAIPerf_TestSuite );
12999 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
13000 {
13001 err = GAIPerfAddBasicTestCases( context );
13002 require_noerr( err, exit );
13003 }
13004 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
13005 {
13006 err = GAIPerfAddAdvancedTestCases( context );
13007 require_noerr( err, exit );
13008 }
13009 else
13010 {
13011 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
13012 goto exit;
13013 }
13014
13015 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
13016 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
13017
13018 signal( SIGINT, SIG_IGN );
13019 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
13020 &context->sigIntSource );
13021 require_noerr( err, exit );
13022 dispatch_resume( context->sigIntSource );
13023
13024 signal( SIGTERM, SIG_IGN );
13025 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
13026 &context->sigTermSource );
13027 require_noerr( err, exit );
13028 dispatch_resume( context->sigTermSource );
13029
13030 GAITesterStart( context->tester );
13031 dispatch_main();
13032
13033 exit:
13034 if( context ) GAIPerfContextFree( context );
13035 exit( 1 );
13036 }
13037
13038 //===========================================================================================================================
13039 // GAIPerfContextFree
13040 //===========================================================================================================================
13041
13042 static void GAIPerfContextFree( GAIPerfContext *inContext )
13043 {
13044 ForgetCF( &inContext->tester );
13045 ForgetCF( &inContext->testCaseResults );
13046 ForgetMem( &inContext->outputFilePath );
13047 dispatch_source_forget( &inContext->sigIntSource );
13048 dispatch_source_forget( &inContext->sigTermSource );
13049 free( inContext );
13050 }
13051
13052 //===========================================================================================================================
13053 // GAIPerfAddAdvancedTestCases
13054 //===========================================================================================================================
13055
13056 #define kTestCaseTitleBufferSize 128
13057
13058 static void
13059 _GAIPerfWriteTestCaseTitle(
13060 char inBuffer[ kTestCaseTitleBufferSize ],
13061 unsigned int inCNAMERecordCount,
13062 unsigned int inARecordCount,
13063 unsigned int inAAAARecordCount,
13064 GAITestAddrType inRequested,
13065 unsigned int inIterationCount,
13066 Boolean inIterationsAreUnique );
13067 static void
13068 _GAIPerfWriteLocalHostTestCaseTitle(
13069 char inBuffer[ kTestCaseTitleBufferSize ],
13070 GAITestAddrType inRequested,
13071 unsigned int inIterationCount );
13072
13073 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
13074 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
13075
13076 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
13077 {
13078 OSStatus err;
13079 unsigned int aliasCount, addressCount, i;
13080 GAITestCase * testCase = NULL;
13081 char title[ kTestCaseTitleBufferSize ];
13082
13083 aliasCount = 0;
13084 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
13085 {
13086 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
13087 {
13088 // Add a test case to resolve a domain name with
13089 //
13090 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
13091 //
13092 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
13093 // requires server queries.
13094
13095 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
13096 inContext->defaultIterCount, true );
13097
13098 err = GAITestCaseCreate( title, &testCase );
13099 require_noerr( err, exit );
13100
13101 for( i = 0; i < inContext->defaultIterCount; ++i )
13102 {
13103 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
13104 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
13105 require_noerr( err, exit );
13106 }
13107
13108 err = GAITesterAddTestCase( inContext->tester, testCase );
13109 require_noerr( err, exit );
13110 testCase = NULL;
13111
13112 // Add a test case to resolve a domain name with
13113 //
13114 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
13115 //
13116 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
13117 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
13118 // ideally require no server queries, i.e., the results should come from the cache.
13119
13120 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
13121 inContext->defaultIterCount, false );
13122
13123 err = GAITestCaseCreate( title, &testCase );
13124 require_noerr( err, exit );
13125
13126 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
13127 kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
13128 require_noerr( err, exit );
13129
13130 err = GAITesterAddTestCase( inContext->tester, testCase );
13131 require_noerr( err, exit );
13132 testCase = NULL;
13133 }
13134
13135 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
13136 }
13137
13138 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
13139
13140 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
13141
13142 err = GAITestCaseCreate( title, &testCase );
13143 require_noerr( err, exit );
13144
13145 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13146 inContext->defaultIterCount );
13147 require_noerr( err, exit );
13148
13149 err = GAITesterAddTestCase( inContext->tester, testCase );
13150 require_noerr( err, exit );
13151 testCase = NULL;
13152
13153 exit:
13154 if( testCase ) GAITestCaseFree( testCase );
13155 return( err );
13156 }
13157
13158 //===========================================================================================================================
13159 // _GAIPerfWriteTestCaseTitle
13160 //===========================================================================================================================
13161
13162 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
13163 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
13164 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
13165 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
13166 "" )
13167
13168 static void
13169 _GAIPerfWriteTestCaseTitle(
13170 char inBuffer[ kTestCaseTitleBufferSize ],
13171 unsigned int inCNAMERecordCount,
13172 unsigned int inARecordCount,
13173 unsigned int inAAAARecordCount,
13174 GAITestAddrType inRequested,
13175 unsigned int inIterationCount,
13176 Boolean inIterationsAreUnique )
13177 {
13178 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
13179 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
13180 inIterationCount, inIterationsAreUnique, ",unique" );
13181 }
13182
13183 //===========================================================================================================================
13184 // _GAIPerfWriteLocalHostTestCaseTitle
13185 //===========================================================================================================================
13186
13187 static void
13188 _GAIPerfWriteLocalHostTestCaseTitle(
13189 char inBuffer[ kTestCaseTitleBufferSize ],
13190 GAITestAddrType inRequested,
13191 unsigned int inIterationCount )
13192 {
13193 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
13194 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
13195 }
13196
13197 //===========================================================================================================================
13198 // GAIPerfAddBasicTestCases
13199 //===========================================================================================================================
13200
13201 #define kGAIPerfBasicTestSuite_AliasCount 2
13202 #define kGAIPerfBasicTestSuite_AddrCount 4
13203
13204 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
13205 {
13206 OSStatus err;
13207 GAITestCase * testCase = NULL;
13208 char title[ kTestCaseTitleBufferSize ];
13209 unsigned int i;
13210
13211 // Test Case #1:
13212 // Resolve a domain name with
13213 //
13214 // 2 CNAME records, 4 A records, and 4 AAAA records
13215 //
13216 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
13217 // queries.
13218
13219 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
13220 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
13221 inContext->defaultIterCount, true );
13222
13223 err = GAITestCaseCreate( title, &testCase );
13224 require_noerr( err, exit );
13225
13226 for( i = 0; i < inContext->defaultIterCount; ++i )
13227 {
13228 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
13229 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
13230 require_noerr( err, exit );
13231 }
13232
13233 err = GAITesterAddTestCase( inContext->tester, testCase );
13234 require_noerr( err, exit );
13235 testCase = NULL;
13236
13237 // Test Case #2:
13238 // Resolve a domain name with
13239 //
13240 // 2 CNAME records, 4 A records, and 4 AAAA records
13241 //
13242 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
13243 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
13244 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
13245
13246 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
13247 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
13248 inContext->defaultIterCount, false );
13249
13250 err = GAITestCaseCreate( title, &testCase );
13251 require_noerr( err, exit );
13252
13253 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
13254 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13255 inContext->defaultIterCount + 1 );
13256 require_noerr( err, exit );
13257
13258 err = GAITesterAddTestCase( inContext->tester, testCase );
13259 require_noerr( err, exit );
13260 testCase = NULL;
13261
13262 // Test Case #3:
13263 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
13264
13265 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
13266
13267 err = GAITestCaseCreate( title, &testCase );
13268 require_noerr( err, exit );
13269
13270 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13271 inContext->defaultIterCount );
13272 require_noerr( err, exit );
13273
13274 err = GAITesterAddTestCase( inContext->tester, testCase );
13275 require_noerr( err, exit );
13276 testCase = NULL;
13277
13278 exit:
13279 if( testCase ) GAITestCaseFree( testCase );
13280 return( err );
13281 }
13282
13283 //===========================================================================================================================
13284 // GAIPerfTesterStopHandler
13285 //===========================================================================================================================
13286
13287 #define kGAIPerfResultsKey_Info CFSTR( "info" )
13288 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
13289 #define kGAIPerfResultsKey_Success CFSTR( "success" )
13290
13291 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
13292 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
13293 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
13294 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
13295
13296 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
13297 {
13298 OSStatus err;
13299 GAIPerfContext * const context = (GAIPerfContext *) inContext;
13300 CFPropertyListRef plist;
13301 int exitCode;
13302
13303 err = inError;
13304 require_noerr_quiet( err, exit );
13305
13306 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13307 "{"
13308 "%kO=" // info
13309 "{"
13310 "%kO=%lli" // callDelayMs
13311 "%kO=%lli" // serverDelayMs
13312 "%kO=%b" // skippedPathEval
13313 "%kO=%b" // usedBadUPDMode
13314 "}"
13315 "%kO=%O" // testCases
13316 "%kO=%b" // success
13317 "}",
13318 kGAIPerfResultsKey_Info,
13319 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
13320 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
13321 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
13322 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
13323 kGAIPerfResultsKey_TestCases, context->testCaseResults,
13324 kGAIPerfResultsKey_Success, !context->testFailed );
13325 require_noerr( err, exit );
13326
13327 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
13328 CFRelease( plist );
13329 require_noerr( err, exit );
13330
13331 exit:
13332 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
13333 GAIPerfContextFree( context );
13334 exit( exitCode );
13335 }
13336
13337 //===========================================================================================================================
13338 // GAIPerfResultsHandler
13339 //===========================================================================================================================
13340
13341 // Keys for test case dictionary
13342
13343 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
13344 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
13345 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
13346 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
13347 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
13348 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
13349 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
13350
13351 // Keys for test case results array entry dictionaries
13352
13353 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
13354 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
13355 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
13356 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
13357
13358 // Keys for test case stats dictionaries
13359
13360 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
13361 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
13362 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
13363 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
13364 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
13365
13366 typedef struct
13367 {
13368 double min;
13369 double max;
13370 double mean;
13371 double stdDev;
13372
13373 } GAIPerfStats;
13374
13375 #define GAIPerfStatsInit( X ) \
13376 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
13377
13378 static void
13379 GAIPerfResultsHandler(
13380 const char * inCaseTitle,
13381 NanoTime64 inCaseStartTime,
13382 NanoTime64 inCaseEndTime,
13383 const GAITestItemResult * inResultArray,
13384 size_t inResultCount,
13385 void * inContext )
13386 {
13387 OSStatus err;
13388 GAIPerfContext * const context = (GAIPerfContext *) inContext;
13389 int namesAreDynamic, namesAreUnique;
13390 const char * ptr;
13391 size_t startIndex;
13392 CFMutableArrayRef results = NULL;
13393 GAIPerfStats stats, firstStats, connStats;
13394 double sum, firstSum, connSum;
13395 size_t keyValueLen, i;
13396 uint32_t count;
13397 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
13398 char startTime[ 32 ];
13399 char endTime[ 32 ];
13400 const GAITestItemResult * result;
13401
13402 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
13403 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
13404 // the domain name's CNAME, A, and AAAA records.
13405
13406 namesAreDynamic = false;
13407 namesAreUnique = false;
13408 ptr = inCaseTitle;
13409 while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
13410 {
13411 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
13412 {
13413 namesAreDynamic = true;
13414 }
13415 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
13416 {
13417 namesAreUnique = true;
13418 }
13419 if( namesAreDynamic && namesAreUnique ) break;
13420 }
13421
13422 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
13423 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
13424 require_action( results, exit, err = kNoMemoryErr );
13425
13426 GAIPerfStatsInit( &stats );
13427 GAIPerfStatsInit( &firstStats );
13428 GAIPerfStatsInit( &connStats );
13429
13430 sum = 0.0;
13431 firstSum = 0.0;
13432 connSum = 0.0;
13433 count = 0;
13434 for( i = startIndex; i < inResultCount; ++i )
13435 {
13436 double value;
13437
13438 result = &inResultArray[ i ];
13439
13440 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
13441 "{"
13442 "%kO=%s" // name
13443 "%kO=%lli" // connectionTimeUs
13444 "%kO=%lli" // firstTimeUs
13445 "%kO=%lli" // timeUs
13446 "%kO=%lli" // error
13447 "}",
13448 kGAIPerfTestCaseResultKey_Name, result->name,
13449 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
13450 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
13451 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
13452 CFSTR( "error" ), (int64_t) result->error );
13453 require_noerr( err, exit );
13454
13455 if( !result->error )
13456 {
13457 value = (double) result->timeUs;
13458 if( value < stats.min ) stats.min = value;
13459 if( value > stats.max ) stats.max = value;
13460 sum += value;
13461
13462 value = (double) result->firstTimeUs;
13463 if( value < firstStats.min ) firstStats.min = value;
13464 if( value > firstStats.max ) firstStats.max = value;
13465 firstSum += value;
13466
13467 value = (double) result->connectionTimeUs;
13468 if( value < connStats.min ) connStats.min = value;
13469 if( value > connStats.max ) connStats.max = value;
13470 connSum += value;
13471
13472 ++count;
13473 }
13474 else
13475 {
13476 context->testFailed = true;
13477 }
13478 }
13479
13480 if( count > 0 )
13481 {
13482 stats.mean = sum / (double) count;
13483 firstStats.mean = firstSum / (double) count;
13484 connStats.mean = connSum / (double) count;
13485
13486 sum = 0.0;
13487 firstSum = 0.0;
13488 connSum = 0.0;
13489 for( i = startIndex; i < inResultCount; ++i )
13490 {
13491 double diff;
13492
13493 result = &inResultArray[ i ];
13494 if( result->error ) continue;
13495
13496 diff = stats.mean - (double) result->timeUs;
13497 sum += ( diff * diff );
13498
13499 diff = firstStats.mean - (double) result->firstTimeUs;
13500 firstSum += ( diff * diff );
13501
13502 diff = connStats.mean - (double) result->connectionTimeUs;
13503 connSum += ( diff * diff );
13504 }
13505 stats.stdDev = sqrt( sum / (double) count );
13506 firstStats.stdDev = sqrt( firstSum / (double) count );
13507 connStats.stdDev = sqrt( connSum / (double) count );
13508 }
13509
13510 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
13511 "{"
13512 "%kO=%s"
13513 "%kO=%s"
13514 "%kO=%s"
13515 "%kO=%O"
13516 "%kO="
13517 "{"
13518 "%kO=%lli"
13519 "%kO=%f"
13520 "%kO=%f"
13521 "%kO=%f"
13522 "%kO=%f"
13523 "}"
13524 "%kO="
13525 "{"
13526 "%kO=%lli"
13527 "%kO=%f"
13528 "%kO=%f"
13529 "%kO=%f"
13530 "%kO=%f"
13531 "}"
13532 "%kO="
13533 "{"
13534 "%kO=%lli"
13535 "%kO=%f"
13536 "%kO=%f"
13537 "%kO=%f"
13538 "%kO=%f"
13539 "}"
13540 "}",
13541 kGAIPerfTestCaseKey_Title, inCaseTitle,
13542 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
13543 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
13544 kGAIPerfTestCaseKey_Results, results,
13545 kGAIPerfTestCaseKey_Stats,
13546 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
13547 kGAIPerfTestCaseStatsKey_Min, stats.min,
13548 kGAIPerfTestCaseStatsKey_Max, stats.max,
13549 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
13550 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
13551 kGAIPerfTestCaseKey_FirstStats,
13552 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
13553 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
13554 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
13555 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
13556 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
13557 kGAIPerfTestCaseKey_ConnectionStats,
13558 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
13559 kGAIPerfTestCaseStatsKey_Min, connStats.min,
13560 kGAIPerfTestCaseStatsKey_Max, connStats.max,
13561 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
13562 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
13563 require_noerr( err, exit );
13564
13565 exit:
13566 CFReleaseNullSafe( results );
13567 if( err ) exit( 1 );
13568 }
13569
13570 //===========================================================================================================================
13571 // GAIPerfSignalHandler
13572 //===========================================================================================================================
13573
13574 static void GAIPerfSignalHandler( void *inContext )
13575 {
13576 GAIPerfContext * const context = (GAIPerfContext *) inContext;
13577
13578 if( !context->tester ) exit( 1 );
13579 GAITesterStop( context->tester );
13580 context->tester = NULL;
13581 }
13582
13583 //===========================================================================================================================
13584 // GAITesterCreate
13585 //===========================================================================================================================
13586
13587 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
13588 // possible strings to use in the Tag label.
13589
13590 #define kGAITesterTagStringLen 6
13591
13592 typedef struct GAITestItem GAITestItem;
13593 struct GAITestItem
13594 {
13595 GAITestItem * next; // Next test item in list.
13596 char * name; // Domain name to resolve.
13597 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
13598 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
13599 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
13600 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
13601 OSStatus error; // Current status/error.
13602 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
13603 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
13604 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
13605 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
13606 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
13607 };
13608
13609 struct GAITestCase
13610 {
13611 GAITestCase * next; // Next test case in list.
13612 GAITestItem * itemList; // List of test items.
13613 char * title; // Title of the test case.
13614 };
13615
13616 struct GAITesterPrivate
13617 {
13618 CFRuntimeBase base; // CF object base.
13619 dispatch_queue_t queue; // Serial work queue.
13620 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
13621 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
13622 GAITestCase * caseList; // List of test cases.
13623 GAITestCase * currentCase; // Pointer to the current test case.
13624 GAITestItem * currentItem; // Pointer to the current test item.
13625 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
13626 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
13627 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
13628 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
13629 Boolean stopped; // True if the tester has been stopped.
13630 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
13631 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
13632 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
13633 pid_t serverPID; // PID of the test DNS server.
13634 int serverDelayMs; // Additional time to have the server delay its responses by.
13635 int serverDefaultTTL; // Default TTL for the server's records.
13636 GAITesterStopHandler_f stopHandler; // User's stop handler.
13637 void * stopContext; // User's event handler context.
13638 GAITesterResultsHandler_f resultsHandler; // User's results handler.
13639 void * resultsContext; // User's results handler context.
13640
13641 // Variables for current test item.
13642
13643 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
13644 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
13645 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
13646 uint64_t connTicks; // Ticks when the connection was created.
13647 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
13648 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
13649 Boolean gotFirstResult; // True if the first result has been received.
13650 };
13651
13652 CF_CLASS_DEFINE( GAITester );
13653
13654 static void _GAITesterStartNextTest( GAITesterRef inTester );
13655 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
13656 static void _GAITesterFirstGAITimeout( void *inContext );
13657 static void _GAITesterTimeout( void *inContext );
13658 static void DNSSD_API
13659 _GAITesterFirstGAICallback(
13660 DNSServiceRef inSDRef,
13661 DNSServiceFlags inFlags,
13662 uint32_t inInterfaceIndex,
13663 DNSServiceErrorType inError,
13664 const char * inHostname,
13665 const struct sockaddr * inSockAddr,
13666 uint32_t inTTL,
13667 void * inContext );
13668 static void DNSSD_API
13669 _GAITesterGetAddrInfoCallback(
13670 DNSServiceRef inSDRef,
13671 DNSServiceFlags inFlags,
13672 uint32_t inInterfaceIndex,
13673 DNSServiceErrorType inError,
13674 const char * inHostname,
13675 const struct sockaddr * inSockAddr,
13676 uint32_t inTTL,
13677 void * inContext );
13678 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
13679
13680 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
13681
13682 static OSStatus
13683 GAITestItemCreate(
13684 const char * inName,
13685 unsigned int inAddressCount,
13686 GAITestAddrType inHasAddrs,
13687 GAITestAddrType inWantAddrs,
13688 unsigned int inTimeLimitMs,
13689 GAITestItem ** outItem );
13690 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
13691 static void GAITestItemFree( GAITestItem *inItem );
13692
13693 static OSStatus
13694 GAITesterCreate(
13695 dispatch_queue_t inQueue,
13696 unsigned int inCallDelayMs,
13697 int inServerDelayMs,
13698 int inServerDefaultTTL,
13699 Boolean inSkipPathEvaluation,
13700 Boolean inBadUDPMode,
13701 GAITesterRef * outTester )
13702 {
13703 OSStatus err;
13704 GAITesterRef obj = NULL;
13705
13706 CF_OBJECT_CREATE( GAITester, obj, err, exit );
13707
13708 ReplaceDispatchQueue( &obj->queue, inQueue );
13709 obj->callDelayMs = inCallDelayMs;
13710 obj->serverPID = -1;
13711 obj->serverDelayMs = inServerDelayMs;
13712 obj->serverDefaultTTL = inServerDefaultTTL;
13713 obj->skipPathEval = inSkipPathEvaluation;
13714 obj->badUDPMode = inBadUDPMode;
13715
13716 *outTester = obj;
13717 obj = NULL;
13718 err = kNoErr;
13719
13720 exit:
13721 CFReleaseNullSafe( obj );
13722 return( err );
13723 }
13724
13725 //===========================================================================================================================
13726 // _GAITesterFinalize
13727 //===========================================================================================================================
13728
13729 static void _GAITesterFinalize( CFTypeRef inObj )
13730 {
13731 GAITesterRef const me = (GAITesterRef) inObj;
13732 GAITestCase * testCase;
13733
13734 check( !me->getAddrInfo );
13735 check( !me->connection );
13736 check( !me->timer );
13737 dispatch_forget( &me->queue );
13738 while( ( testCase = me->caseList ) != NULL )
13739 {
13740 me->caseList = testCase->next;
13741 GAITestCaseFree( testCase );
13742 }
13743 }
13744
13745 //===========================================================================================================================
13746 // GAITesterStart
13747 //===========================================================================================================================
13748
13749 static void _GAITesterStart( void *inContext );
13750 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
13751
13752 static void GAITesterStart( GAITesterRef me )
13753 {
13754 CFRetain( me );
13755 dispatch_async_f( me->queue, me, _GAITesterStart );
13756 }
13757
13758 #define kGAITesterFirstGAITimeoutSecs 4
13759
13760 static void _GAITesterStart( void *inContext )
13761 {
13762 OSStatus err;
13763 GAITesterRef const me = (GAITesterRef) inContext;
13764 DNSServiceFlags flags;
13765 char name[ 64 ];
13766 char tag[ kGAITesterTagStringLen + 1 ];
13767
13768 err = _SpawnCommand( &me->serverPID, NULL, NULL, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
13769 (int64_t) getpid(),
13770 me->serverDefaultTTL >= 0, " --defaultTTL ",
13771 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
13772 me->serverDelayMs >= 0, " --responseDelay ",
13773 me->serverDelayMs >= 0, me->serverDelayMs,
13774 me->badUDPMode, " --badUDPMode" );
13775 require_noerr_quiet( err, exit );
13776
13777 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test.",
13778 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
13779
13780 flags = 0;
13781 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
13782
13783 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
13784 _GAITesterFirstGAICallback, me );
13785 require_noerr( err, exit );
13786
13787 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
13788 require_noerr( err, exit );
13789
13790 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
13791 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
13792 _GAITesterFirstGAITimeout, me, &me->timer );
13793 require_noerr( err, exit );
13794 dispatch_resume( me->timer );
13795
13796 exit:
13797 if( err ) _GAITesterStop( me, err );
13798 }
13799
13800 //===========================================================================================================================
13801 // GAITesterStop
13802 //===========================================================================================================================
13803
13804 static void _GAITesterUserStop( void *inContext );
13805
13806 static void GAITesterStop( GAITesterRef me )
13807 {
13808 CFRetain( me );
13809 dispatch_async_f( me->queue, me, _GAITesterUserStop );
13810 }
13811
13812 static void _GAITesterUserStop( void *inContext )
13813 {
13814 GAITesterRef const me = (GAITesterRef) inContext;
13815
13816 _GAITesterStop( me, kCanceledErr );
13817 CFRelease( me );
13818 }
13819
13820 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
13821 {
13822 OSStatus err;
13823
13824 ForgetPacketCapture( &me->pcap );
13825 dispatch_source_forget( &me->timer );
13826 DNSServiceForget( &me->getAddrInfo );
13827 DNSServiceForget( &me->connection );
13828 if( me->serverPID != -1 )
13829 {
13830 err = kill( me->serverPID, SIGTERM );
13831 err = map_global_noerr_errno( err );
13832 check_noerr( err );
13833 me->serverPID = -1;
13834 }
13835
13836 if( !me->stopped )
13837 {
13838 me->stopped = true;
13839 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
13840 CFRelease( me );
13841 }
13842 }
13843
13844 //===========================================================================================================================
13845 // GAITesterAddTestCase
13846 //===========================================================================================================================
13847
13848 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
13849 {
13850 OSStatus err;
13851 GAITestCase ** ptr;
13852
13853 require_action_quiet( inCase->itemList, exit, err = kCountErr );
13854
13855 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
13856 *ptr = inCase;
13857 err = kNoErr;
13858
13859 exit:
13860 return( err );
13861 }
13862
13863 //===========================================================================================================================
13864 // GAITesterSetStopHandler
13865 //===========================================================================================================================
13866
13867 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
13868 {
13869 me->stopHandler = inStopHandler;
13870 me->stopContext = inStopContext;
13871 }
13872
13873 //===========================================================================================================================
13874 // GAITesterSetResultsHandler
13875 //===========================================================================================================================
13876
13877 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
13878 {
13879 me->resultsHandler = inResultsHandler;
13880 me->resultsContext = inResultsContext;
13881 }
13882
13883 //===========================================================================================================================
13884 // _GAITesterStartNextTest
13885 //===========================================================================================================================
13886
13887 static void _GAITesterStartNextTest( GAITesterRef me )
13888 {
13889 OSStatus err;
13890 GAITestItem * item;
13891 DNSServiceFlags flags;
13892 DNSServiceProtocol protocols;
13893 int done = false;
13894
13895 if( me->currentItem ) me->currentItem = me->currentItem->next;
13896
13897 if( !me->currentItem )
13898 {
13899 if( me->currentCase )
13900 {
13901 // No more test items means that the current test case has completed.
13902
13903 me->caseEndTime = NanoTimeGetCurrent();
13904
13905 if( me->resultsHandler )
13906 {
13907 size_t resultCount, i;
13908 GAITestItemResult * resultArray;
13909
13910 resultCount = 0;
13911 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
13912 check( resultCount > 0 );
13913
13914 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
13915 require_action( resultArray, exit, err = kNoMemoryErr );
13916
13917 item = me->currentCase->itemList;
13918 for( i = 0; i < resultCount; ++i )
13919 {
13920 resultArray[ i ].name = item->name;
13921 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
13922 resultArray[ i ].firstTimeUs = item->firstTimeUs;
13923 resultArray[ i ].timeUs = item->timeUs;
13924 resultArray[ i ].error = item->error;
13925 item = item->next;
13926 }
13927 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
13928 me->resultsContext );
13929 ForgetMem( &resultArray );
13930 }
13931
13932 me->currentCase = me->currentCase->next;
13933 if( !me->currentCase )
13934 {
13935 done = true;
13936 err = kNoErr;
13937 goto exit;
13938 }
13939 }
13940 else
13941 {
13942 me->currentCase = me->caseList;
13943 }
13944 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
13945 me->currentItem = me->currentCase->itemList;
13946 }
13947
13948 item = me->currentItem;
13949 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
13950
13951 if( !item->wantV4 ) me->bitmapV4 = 0;
13952 else if( !item->hasV4 ) me->bitmapV4 = 1;
13953 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
13954 else me->bitmapV4 = ~UINT64_C( 0 );
13955
13956 if( !item->wantV6 ) me->bitmapV6 = 0;
13957 else if( !item->hasV6 ) me->bitmapV6 = 1;
13958 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
13959 else me->bitmapV6 = ~UINT64_C( 0 );
13960 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
13961 me->gotFirstResult = false;
13962
13963 // Perform preliminary tasks if this is the start of a new test case.
13964
13965 if( item == me->currentCase->itemList )
13966 {
13967 // Flush mDNSResponder's cache.
13968
13969 err = systemf( NULL, "killall -HUP mDNSResponder" );
13970 require_noerr( err, exit );
13971 sleep( 1 );
13972
13973 me->caseStartTime = NanoTimeGetCurrent();
13974 me->caseEndTime = kNanoTime_Invalid;
13975 }
13976
13977 // Start a packet capture.
13978
13979 check( !me->pcap );
13980 err = _GAITesterCreatePacketCapture( &me->pcap );
13981 require_noerr( err, exit );
13982
13983 // Start timer for test item's time limit.
13984
13985 check( !me->timer );
13986 if( item->timeLimitMs > 0 )
13987 {
13988 unsigned int timeLimitMs;
13989
13990 timeLimitMs = item->timeLimitMs;
13991 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
13992 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
13993
13994 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
13995 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
13996 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
13997 require_noerr( err, exit );
13998 dispatch_resume( me->timer );
13999 }
14000
14001 // Call DNSServiceGetAddrInfo().
14002
14003 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
14004
14005 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
14006 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
14007
14008 protocols = 0;
14009 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
14010 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
14011
14012 me->startTicks = UpTicks();
14013
14014 check( !me->connection );
14015 err = DNSServiceCreateConnection( &me->connection );
14016 require_noerr( err, exit );
14017
14018 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
14019 require_noerr( err, exit );
14020
14021 me->connTicks = UpTicks();
14022
14023 check( !me->getAddrInfo );
14024 me->getAddrInfo = me->connection;
14025 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
14026 _GAITesterGetAddrInfoCallback, me );
14027 require_noerr( err, exit );
14028
14029 exit:
14030 if( err || done ) _GAITesterStop( me, err );
14031 }
14032
14033 //===========================================================================================================================
14034 // _GAITesterCreatePacketCapture
14035 //===========================================================================================================================
14036
14037 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
14038 {
14039 OSStatus err;
14040 pcap_t * pcap;
14041 struct bpf_program program;
14042 char errBuf[ PCAP_ERRBUF_SIZE ];
14043
14044 pcap = pcap_create( "lo0", errBuf );
14045 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
14046
14047 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
14048 require_noerr_action( err, exit, err = kUnknownErr );
14049
14050 err = pcap_set_snaplen( pcap, 512 );
14051 require_noerr_action( err, exit, err = kUnknownErr );
14052
14053 err = pcap_set_immediate_mode( pcap, 0 );
14054 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14055
14056 err = pcap_activate( pcap );
14057 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14058
14059 err = pcap_setdirection( pcap, PCAP_D_INOUT );
14060 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14061
14062 err = pcap_setnonblock( pcap, 1, errBuf );
14063 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
14064
14065 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
14066 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14067
14068 err = pcap_setfilter( pcap, &program );
14069 pcap_freecode( &program );
14070 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14071
14072 *outPCap = pcap;
14073 pcap = NULL;
14074
14075 exit:
14076 if( pcap ) pcap_close( pcap );
14077 return( err );
14078 }
14079
14080 //===========================================================================================================================
14081 // _GAITesterFirstGAITimeout
14082 //===========================================================================================================================
14083
14084 static void _GAITesterFirstGAITimeout( void *inContext )
14085 {
14086 GAITesterRef const me = (GAITesterRef) inContext;
14087
14088 _GAITesterStop( me, kNoResourcesErr );
14089 }
14090
14091 //===========================================================================================================================
14092 // _GAITesterTimeout
14093 //===========================================================================================================================
14094
14095 static void _GAITesterTimeout( void *inContext )
14096 {
14097 GAITesterRef const me = (GAITesterRef) inContext;
14098
14099 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
14100 }
14101
14102 //===========================================================================================================================
14103 // _GAITesterFirstGAICallback
14104 //===========================================================================================================================
14105
14106 static void DNSSD_API
14107 _GAITesterFirstGAICallback(
14108 DNSServiceRef inSDRef,
14109 DNSServiceFlags inFlags,
14110 uint32_t inInterfaceIndex,
14111 DNSServiceErrorType inError,
14112 const char * inHostname,
14113 const struct sockaddr * inSockAddr,
14114 uint32_t inTTL,
14115 void * inContext )
14116 {
14117 GAITesterRef const me = (GAITesterRef) inContext;
14118
14119 Unused( inSDRef );
14120 Unused( inInterfaceIndex );
14121 Unused( inHostname );
14122 Unused( inSockAddr );
14123 Unused( inTTL );
14124
14125 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
14126 {
14127 dispatch_source_forget( &me->timer );
14128 DNSServiceForget( &me->getAddrInfo );
14129
14130 _GAITesterStartNextTest( me );
14131 }
14132 }
14133
14134 //===========================================================================================================================
14135 // _GAITesterGetAddrInfoCallback
14136 //===========================================================================================================================
14137
14138 static void DNSSD_API
14139 _GAITesterGetAddrInfoCallback(
14140 DNSServiceRef inSDRef,
14141 DNSServiceFlags inFlags,
14142 uint32_t inInterfaceIndex,
14143 DNSServiceErrorType inError,
14144 const char * inHostname,
14145 const struct sockaddr * inSockAddr,
14146 uint32_t inTTL,
14147 void * inContext )
14148 {
14149 OSStatus err;
14150 GAITesterRef const me = (GAITesterRef) inContext;
14151 GAITestItem * const item = me->currentItem;
14152 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
14153 uint64_t nowTicks;
14154 uint64_t * bitmapPtr;
14155 uint64_t bitmask;
14156 int hasAddr;
14157
14158 Unused( inSDRef );
14159 Unused( inInterfaceIndex );
14160 Unused( inHostname );
14161 Unused( inTTL );
14162
14163 nowTicks = UpTicks();
14164
14165 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
14166
14167 // Check if we were expecting an IP address result of this type.
14168
14169 if( sip->sa.sa_family == AF_INET )
14170 {
14171 bitmapPtr = &me->bitmapV4;
14172 hasAddr = item->hasV4;
14173 }
14174 else if( sip->sa.sa_family == AF_INET6 )
14175 {
14176 bitmapPtr = &me->bitmapV6;
14177 hasAddr = item->hasV6;
14178 }
14179 else
14180 {
14181 err = kTypeErr;
14182 goto exit;
14183 }
14184
14185 bitmask = 0;
14186 if( hasAddr )
14187 {
14188 uint32_t addrOffset;
14189
14190 require_noerr_action_quiet( inError, exit, err = inError );
14191
14192 if( sip->sa.sa_family == AF_INET )
14193 {
14194 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
14195
14196 if( strcasecmp( item->name, "localhost." ) == 0 )
14197 {
14198 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
14199 }
14200 else
14201 {
14202 addrOffset = addrV4 - kDNSServerBaseAddrV4;
14203 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
14204 {
14205 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
14206 }
14207 }
14208 }
14209 else
14210 {
14211 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
14212
14213 if( strcasecmp( item->name, "localhost." ) == 0 )
14214 {
14215 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
14216 }
14217 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
14218 {
14219 addrOffset = addrV6[ 15 ];
14220 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
14221 {
14222 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
14223 }
14224 }
14225 }
14226 }
14227 else
14228 {
14229 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
14230 bitmask = 1;
14231 }
14232 require_action_quiet( bitmask != 0, exit, err = kValueErr );
14233 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
14234
14235 *bitmapPtr &= ~bitmask;
14236 if( !me->gotFirstResult )
14237 {
14238 me->firstTicks = nowTicks;
14239 me->gotFirstResult = true;
14240 }
14241 err = kNoErr;
14242
14243 exit:
14244 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
14245 {
14246 me->endTicks = nowTicks;
14247 _GAITesterCompleteCurrentTest( me, err );
14248 }
14249 }
14250
14251 //===========================================================================================================================
14252 // _GAITesterCompleteCurrentTest
14253 //===========================================================================================================================
14254
14255 static OSStatus
14256 _GAITesterGetDNSMessageFromPacket(
14257 const uint8_t * inPacketPtr,
14258 size_t inPacketLen,
14259 const uint8_t ** outMsgPtr,
14260 size_t * outMsgLen );
14261
14262 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
14263 {
14264 OSStatus err;
14265 GAITestItem * const item = me->currentItem;
14266 struct timeval timeStamps[ 4 ];
14267 struct timeval * tsPtr;
14268 struct timeval * tsQA = NULL;
14269 struct timeval * tsQAAAA = NULL;
14270 struct timeval * tsRA = NULL;
14271 struct timeval * tsRAAAA = NULL;
14272 struct timeval * t1;
14273 struct timeval * t2;
14274 int64_t idleTimeUs;
14275 uint8_t name[ kDomainNameLengthMax ];
14276
14277 dispatch_source_forget( &me->timer );
14278 DNSServiceForget( &me->getAddrInfo );
14279 DNSServiceForget( &me->connection );
14280
14281 item->error = inError;
14282 if( item->error )
14283 {
14284 err = kNoErr;
14285 goto exit;
14286 }
14287
14288 err = DomainNameFromString( name, item->name, NULL );
14289 require_noerr( err, exit );
14290
14291 tsPtr = &timeStamps[ 0 ];
14292 for( ;; )
14293 {
14294 int status;
14295 struct pcap_pkthdr * pktHdr;
14296 const uint8_t * packet;
14297 const uint8_t * msgPtr;
14298 size_t msgLen;
14299 const DNSHeader * hdr;
14300 unsigned int flags;
14301 const uint8_t * ptr;
14302 uint16_t qtype, qclass;
14303 uint8_t qname[ kDomainNameLengthMax ];
14304
14305 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
14306 if( status != 1 ) break;
14307 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
14308 if( msgLen < kDNSHeaderLength ) continue;
14309
14310 hdr = (const DNSHeader *) msgPtr;
14311 flags = DNSHeaderGetFlags( hdr );
14312 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
14313 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
14314
14315 ptr = (const uint8_t *) &hdr[ 1 ];
14316 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
14317 if( qclass != kDNSServiceClass_IN ) continue;
14318 if( !DomainNameEqual( qname, name ) ) continue;
14319
14320 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
14321 {
14322 if( flags & kDNSHeaderFlag_Response )
14323 {
14324 if( tsQA && !tsRA )
14325 {
14326 tsRA = tsPtr++;
14327 *tsRA = pktHdr->ts;
14328 }
14329 }
14330 else if( !tsQA )
14331 {
14332 tsQA = tsPtr++;
14333 *tsQA = pktHdr->ts;
14334 }
14335 }
14336 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
14337 {
14338 if( flags & kDNSHeaderFlag_Response )
14339 {
14340 if( tsQAAAA && !tsRAAAA )
14341 {
14342 tsRAAAA = tsPtr++;
14343 *tsRAAAA = pktHdr->ts;
14344 }
14345 }
14346 else if( !tsQAAAA )
14347 {
14348 tsQAAAA = tsPtr++;
14349 *tsQAAAA = pktHdr->ts;
14350 }
14351 }
14352 }
14353
14354 // t1 is the time when the last query was sent.
14355
14356 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
14357 else t1 = tsQA ? tsQA : tsQAAAA;
14358
14359 // t2 is when the first response was received.
14360
14361 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
14362 else t2 = tsRA ? tsRA : tsRAAAA;
14363
14364 if( t1 && t2 )
14365 {
14366 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
14367 if( idleTimeUs < 0 ) idleTimeUs = 0;
14368 }
14369 else
14370 {
14371 idleTimeUs = 0;
14372 }
14373
14374 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
14375 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
14376 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
14377
14378 exit:
14379 ForgetPacketCapture( &me->pcap );
14380 if( err ) _GAITesterStop( me, err );
14381 else _GAITesterStartNextTest( me );
14382 }
14383
14384 //===========================================================================================================================
14385 // _GAITesterGetDNSMessageFromPacket
14386 //===========================================================================================================================
14387
14388 #define kHeaderSizeNullLink 4
14389 #define kHeaderSizeIPv4Min 20
14390 #define kHeaderSizeIPv6 40
14391 #define kHeaderSizeUDP 8
14392
14393 #define kIPProtocolUDP 0x11
14394
14395 static OSStatus
14396 _GAITesterGetDNSMessageFromPacket(
14397 const uint8_t * inPacketPtr,
14398 size_t inPacketLen,
14399 const uint8_t ** outMsgPtr,
14400 size_t * outMsgLen )
14401 {
14402 OSStatus err;
14403 const uint8_t * nullLink;
14404 uint32_t addressFamily;
14405 const uint8_t * ip;
14406 int ipHeaderLen;
14407 int protocol;
14408 const uint8_t * msg;
14409 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
14410
14411 nullLink = &inPacketPtr[ 0 ];
14412 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
14413 addressFamily = ReadHost32( &nullLink[ 0 ] );
14414
14415 ip = &nullLink[ kHeaderSizeNullLink ];
14416 if( addressFamily == AF_INET )
14417 {
14418 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
14419 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
14420 protocol = ip[ 9 ];
14421 }
14422 else if( addressFamily == AF_INET6 )
14423 {
14424 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
14425 ipHeaderLen = kHeaderSizeIPv6;
14426 protocol = ip[ 6 ];
14427 }
14428 else
14429 {
14430 err = kTypeErr;
14431 goto exit;
14432 }
14433 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
14434 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
14435
14436 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
14437
14438 *outMsgPtr = msg;
14439 *outMsgLen = (size_t)( end - msg );
14440 err = kNoErr;
14441
14442 exit:
14443 return( err );
14444 }
14445
14446 //===========================================================================================================================
14447 // GAITestCaseCreate
14448 //===========================================================================================================================
14449
14450 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
14451 {
14452 OSStatus err;
14453 GAITestCase * obj;
14454
14455 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
14456 require_action( obj, exit, err = kNoMemoryErr );
14457
14458 obj->title = strdup( inTitle );
14459 require_action( obj->title, exit, err = kNoMemoryErr );
14460
14461 *outCase = obj;
14462 obj = NULL;
14463 err = kNoErr;
14464
14465 exit:
14466 if( obj ) GAITestCaseFree( obj );
14467 return( err );
14468 }
14469
14470 //===========================================================================================================================
14471 // GAITestCaseFree
14472 //===========================================================================================================================
14473
14474 static void GAITestCaseFree( GAITestCase *inCase )
14475 {
14476 GAITestItem * item;
14477
14478 while( ( item = inCase->itemList ) != NULL )
14479 {
14480 inCase->itemList = item->next;
14481 GAITestItemFree( item );
14482 }
14483 ForgetMem( &inCase->title );
14484 free( inCase );
14485 }
14486
14487 //===========================================================================================================================
14488 // GAITestCaseAddItem
14489 //===========================================================================================================================
14490
14491 static OSStatus
14492 GAITestCaseAddItem(
14493 GAITestCase * inCase,
14494 unsigned int inAliasCount,
14495 unsigned int inAddressCount,
14496 int inTTL,
14497 GAITestAddrType inHasAddrs,
14498 GAITestAddrType inWantAddrs,
14499 unsigned int inTimeLimitMs,
14500 unsigned int inItemCount )
14501 {
14502 OSStatus err;
14503 GAITestItem * item;
14504 GAITestItem * item2;
14505 GAITestItem * newItemList = NULL;
14506 GAITestItem ** itemPtr;
14507 char * ptr;
14508 char * end;
14509 unsigned int i;
14510 char name[ 64 ];
14511 char tag[ kGAITesterTagStringLen + 1 ];
14512
14513 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
14514
14515 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
14516
14517 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
14518 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
14519 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
14520
14521 ptr = &name[ 0 ];
14522 end = &name[ countof( name ) ];
14523
14524 // Add Alias label.
14525
14526 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
14527 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
14528
14529 // Add Count label.
14530
14531 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
14532
14533 // Add TTL label.
14534
14535 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
14536
14537 // Add Tag label.
14538
14539 SNPrintF_Add( &ptr, end, "tag-%s.",
14540 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14541
14542 // Add IPv4 or IPv6 label if necessary.
14543
14544 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
14545 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
14546
14547 // Finally, add the d.test. labels.
14548
14549 SNPrintF_Add( &ptr, end, "d.test." );
14550
14551 // Create item.
14552
14553 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
14554 require_noerr( err, exit );
14555
14556 newItemList = item;
14557 itemPtr = &item->next;
14558
14559 // Create repeat items.
14560
14561 for( i = 1; i < inItemCount; ++i )
14562 {
14563 err = GAITestItemDup( item, &item2 );
14564 require_noerr( err, exit );
14565
14566 *itemPtr = item2;
14567 itemPtr = &item2->next;
14568 }
14569
14570 // Append to test case's item list.
14571
14572 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
14573 *itemPtr = newItemList;
14574 newItemList = NULL;
14575
14576 exit:
14577 while( ( item = newItemList ) != NULL )
14578 {
14579 newItemList = item->next;
14580 GAITestItemFree( item );
14581 }
14582 return( err );
14583 }
14584
14585 //===========================================================================================================================
14586 // GAITestCaseAddLocalHostItem
14587 //===========================================================================================================================
14588
14589 static OSStatus
14590 GAITestCaseAddLocalHostItem(
14591 GAITestCase * inCase,
14592 GAITestAddrType inWantAddrs,
14593 unsigned int inTimeLimitMs,
14594 unsigned int inItemCount )
14595 {
14596 OSStatus err;
14597 GAITestItem * item;
14598 GAITestItem * item2;
14599 GAITestItem * newItemList = NULL;
14600 GAITestItem ** itemPtr;
14601 unsigned int i;
14602
14603 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
14604
14605 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
14606 require_noerr( err, exit );
14607
14608 newItemList = item;
14609 itemPtr = &item->next;
14610
14611 // Create repeat items.
14612
14613 for( i = 1; i < inItemCount; ++i )
14614 {
14615 err = GAITestItemDup( item, &item2 );
14616 require_noerr( err, exit );
14617
14618 *itemPtr = item2;
14619 itemPtr = &item2->next;
14620 }
14621
14622 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
14623 *itemPtr = newItemList;
14624 newItemList = NULL;
14625
14626 exit:
14627 while( ( item = newItemList ) != NULL )
14628 {
14629 newItemList = item->next;
14630 GAITestItemFree( item );
14631 }
14632 return( err );
14633 }
14634
14635 //===========================================================================================================================
14636 // GAITestItemCreate
14637 //===========================================================================================================================
14638
14639 static OSStatus
14640 GAITestItemCreate(
14641 const char * inName,
14642 unsigned int inAddressCount,
14643 GAITestAddrType inHasAddrs,
14644 GAITestAddrType inWantAddrs,
14645 unsigned int inTimeLimitMs,
14646 GAITestItem ** outItem )
14647 {
14648 OSStatus err;
14649 GAITestItem * obj = NULL;
14650
14651 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
14652 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
14653 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
14654
14655 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
14656 require_action( obj, exit, err = kNoMemoryErr );
14657
14658 obj->name = strdup( inName );
14659 require_action( obj->name, exit, err = kNoMemoryErr );
14660
14661 obj->addressCount = inAddressCount;
14662 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
14663 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
14664 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
14665 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
14666 obj->error = kInProgressErr;
14667 obj->timeLimitMs = inTimeLimitMs;
14668
14669 *outItem = obj;
14670 obj = NULL;
14671 err = kNoErr;
14672
14673 exit:
14674 if( obj ) GAITestItemFree( obj );
14675 return( err );
14676 }
14677
14678 //===========================================================================================================================
14679 // GAITestItemDup
14680 //===========================================================================================================================
14681
14682 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
14683 {
14684 OSStatus err;
14685 GAITestItem * obj;
14686
14687 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
14688 require_action( obj, exit, err = kNoMemoryErr );
14689
14690 *obj = *inItem;
14691 obj->next = NULL;
14692 if( inItem->name )
14693 {
14694 obj->name = strdup( inItem->name );
14695 require_action( obj->name, exit, err = kNoMemoryErr );
14696 }
14697
14698 *outItem = obj;
14699 obj = NULL;
14700 err = kNoErr;
14701
14702 exit:
14703 if( obj ) GAITestItemFree( obj );
14704 return( err );
14705 }
14706
14707 //===========================================================================================================================
14708 // GAITestItemFree
14709 //===========================================================================================================================
14710
14711 static void GAITestItemFree( GAITestItem *inItem )
14712 {
14713 ForgetMem( &inItem->name );
14714 free( inItem );
14715 }
14716
14717 //===========================================================================================================================
14718 // MDNSDiscoveryTestCmd
14719 //===========================================================================================================================
14720
14721 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
14722
14723 typedef struct
14724 {
14725 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
14726 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
14727 NanoTime64 startTime; // When the test started.
14728 NanoTime64 endTime; // When the test ended.
14729 pid_t replierPID; // PID of mDNS replier.
14730 uint32_t ifIndex; // Index of interface to run the replier on.
14731 unsigned int instanceCount; // Desired number of service instances.
14732 unsigned int txtSize; // Desired size of each service instance's TXT record data.
14733 unsigned int recordCountA; // Desired number of A records per replier hostname.
14734 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
14735 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
14736 double ucastDropRate; // Replier's probability of dropping a unicast response.
14737 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
14738 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
14739 Boolean useIPv4; // True if the replier is to use IPv4.
14740 Boolean useIPv6; // True if the replier is to use IPv6.
14741 #if( MDNSRESPONDER_PROJECT )
14742 Boolean useNewGAI; // True if the browser is to use dnssd_getaddrinfo to resolve hostnames.
14743 #endif
14744 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
14745 char * replierCommand; // Command used to run the replier.
14746 char * serviceType; // Type of services to browse for.
14747 ServiceBrowserRef browser; // Service browser.
14748 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
14749 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
14750 OutputFormatType outputFormat; // Format of test results output.
14751 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
14752 char hostname[ 16 + 1 ]; // Base hostname that the replier is to use for instance and host names.
14753 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
14754
14755 } MDNSDiscoveryTestContext;
14756
14757 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
14758 static void DNSSD_API
14759 _MDNSDiscoveryTestAboutQueryCallback(
14760 DNSServiceRef inSDRef,
14761 DNSServiceFlags inFlags,
14762 uint32_t inInterfaceIndex,
14763 DNSServiceErrorType inError,
14764 const char * inFullName,
14765 uint16_t inType,
14766 uint16_t inClass,
14767 uint16_t inRDataLen,
14768 const void * inRDataPtr,
14769 uint32_t inTTL,
14770 void * inContext );
14771 static void
14772 _MDNSDiscoveryTestServiceBrowserCallback(
14773 ServiceBrowserResults * inResults,
14774 OSStatus inError,
14775 void * inContext );
14776 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
14777
14778 static void MDNSDiscoveryTestCmd( void )
14779 {
14780 OSStatus err;
14781 MDNSDiscoveryTestContext * context;
14782 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
14783
14784 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
14785 require_action( context, exit, err = kNoMemoryErr );
14786
14787 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
14788 require_noerr_quiet( err, exit );
14789
14790 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
14791 require_noerr_quiet( err, exit );
14792
14793 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
14794 require_noerr_quiet( err, exit );
14795
14796 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
14797 require_noerr_quiet( err, exit );
14798
14799 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
14800 require_noerr_quiet( err, exit );
14801
14802 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
14803 require_noerr_quiet( err, exit );
14804
14805 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
14806 require_noerr_quiet( err, exit );
14807
14808 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
14809 require_noerr_quiet( err, exit );
14810
14811 context->replierPID = -1;
14812 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
14813 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
14814 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
14815 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
14816 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
14817 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
14818 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
14819 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
14820 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
14821 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
14822 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
14823 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
14824 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
14825 #if( MDNSRESPONDER_PROJECT )
14826 context->useNewGAI = gMDNSDiscoveryTest_UseNewGAI ? true : false;
14827 #endif
14828
14829 if( gMDNSDiscoveryTest_Interface )
14830 {
14831 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
14832 require_noerr_quiet( err, exit );
14833 }
14834 else
14835 {
14836 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14837 require_noerr_quiet( err, exit );
14838 }
14839
14840 err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
14841 require_noerr_quiet( err, exit );
14842
14843 if( gMDNSDiscoveryTest_FlushCache )
14844 {
14845 err = CheckRootUser();
14846 require_noerr_quiet( err, exit );
14847
14848 err = systemf( NULL, "killall -HUP mDNSResponder" );
14849 require_noerr( err, exit );
14850 sleep( 1 );
14851 context->flushedCache = true;
14852 }
14853
14854 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
14855 context->hostname );
14856 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
14857
14858 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
14859 require_action( context->serviceType, exit, err = kUnknownErr );
14860
14861 ASPrintF( &context->replierCommand,
14862 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
14863 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
14864 (int64_t) getpid(),
14865 context->ifIndex,
14866 context->hostname,
14867 context->tag,
14868 context->instanceCount,
14869 context->recordCountA,
14870 context->recordCountAAAA,
14871 context->ucastDropRate,
14872 context->mcastDropRate,
14873 context->maxDropCount,
14874 context->noAdditionals, " --noAdditionals",
14875 context->useIPv4, " --ipv4",
14876 context->useIPv6, " --ipv6" );
14877 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
14878
14879 err = _SpawnCommand( &context->replierPID, NULL, NULL, "%s", context->replierCommand );
14880 require_noerr_quiet( err, exit );
14881
14882 // Query for the replier's about TXT record. A response means that it's fully up and running.
14883
14884 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
14885 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
14886 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
14887 require_noerr( err, exit );
14888
14889 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
14890 require_noerr( err, exit );
14891
14892 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
14893 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
14894 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
14895 require_noerr( err, exit );
14896 dispatch_resume( context->queryTimer );
14897
14898 context->startTime = NanoTimeGetCurrent();
14899 dispatch_main();
14900
14901 exit:
14902 exit( 1 );
14903 }
14904
14905 //===========================================================================================================================
14906 // _MDNSDiscoveryTestFirstQueryTimeout
14907 //===========================================================================================================================
14908
14909 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
14910 {
14911 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
14912
14913 dispatch_source_forget( &context->queryTimer );
14914
14915 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
14916 exit( 1 );
14917 }
14918
14919 //===========================================================================================================================
14920 // _MDNSDiscoveryTestAboutQueryCallback
14921 //===========================================================================================================================
14922
14923 static void DNSSD_API
14924 _MDNSDiscoveryTestAboutQueryCallback(
14925 DNSServiceRef inSDRef,
14926 DNSServiceFlags inFlags,
14927 uint32_t inInterfaceIndex,
14928 DNSServiceErrorType inError,
14929 const char * inFullName,
14930 uint16_t inType,
14931 uint16_t inClass,
14932 uint16_t inRDataLen,
14933 const void * inRDataPtr,
14934 uint32_t inTTL,
14935 void * inContext )
14936 {
14937 OSStatus err;
14938 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
14939
14940 Unused( inSDRef );
14941 Unused( inInterfaceIndex );
14942 Unused( inFullName );
14943 Unused( inType );
14944 Unused( inClass );
14945 Unused( inRDataLen );
14946 Unused( inRDataPtr );
14947 Unused( inTTL );
14948
14949 err = inError;
14950 require_noerr( err, exit );
14951 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
14952
14953 DNSServiceForget( &context->query );
14954 dispatch_source_forget( &context->queryTimer );
14955
14956 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
14957 require_noerr( err, exit );
14958
14959 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
14960 require_noerr( err, exit );
14961
14962 #if( MDNSRESPONDER_PROJECT )
14963 ServiceBrowserSetUseNewGAI( context->browser, context->useNewGAI );
14964 #endif
14965 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
14966 ServiceBrowserStart( context->browser );
14967
14968 exit:
14969 if( err ) exit( 1 );
14970 }
14971
14972 //===========================================================================================================================
14973 // _MDNSDiscoveryTestServiceBrowserCallback
14974 //===========================================================================================================================
14975
14976 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
14977 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
14978 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
14979 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
14980 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
14981 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
14982 #define kMDNSDiscoveryTestResultsKey_UsedNewGAI CFSTR( "usedNewGAI" )
14983 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
14984 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
14985 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
14986 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
14987 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
14988
14989 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
14990 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
14991 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
14992 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
14993 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
14994 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
14995 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
14996 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
14997 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
14998 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
14999
15000 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
15001 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
15002
15003 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
15004 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
15005 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
15006 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
15007 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
15008 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
15009 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
15010
15011 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
15012 {
15013 OSStatus err;
15014 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
15015 const SBRDomain * domain;
15016 const SBRServiceType * type;
15017 const SBRServiceInstance * instance;
15018 const SBRServiceInstance ** instanceArray = NULL;
15019 const SBRIPAddress * ipaddr;
15020 size_t hostnameLen;
15021 const char * ptr;
15022 const char * end;
15023 unsigned int i;
15024 uint32_t u32;
15025 CFMutableArrayRef unexpectedInstances;
15026 CFMutableArrayRef missingInstances;
15027 CFMutableArrayRef incorrectInstances;
15028 CFMutableDictionaryRef plist = NULL;
15029 CFMutableDictionaryRef badDict = NULL;
15030 CFMutableArrayRef unexpectedAddrs = NULL;
15031 CFMutableArrayRef missingAddrs = NULL;
15032 uint64_t maxResolveTimeUs;
15033 int success = false;
15034 char startTime[ 32 ];
15035 char endTime[ 32 ];
15036
15037 context->endTime = NanoTimeGetCurrent();
15038
15039 err = inError;
15040 require_noerr( err, exit );
15041
15042 _NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
15043 _NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
15044 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15045 "{"
15046 "%kO="
15047 "{"
15048 "%kO=%s" // replierCommand
15049 "%kO=%lli" // txtSize
15050 "%kO=%lli" // instanceCount
15051 "%kO=%lli" // recordCountA
15052 "%kO=%lli" // recordCountAAAA
15053 "%kO=%s" // hostname
15054 "%kO=%b" // noAdditionals
15055 "%kO=%f" // ucastDropRate
15056 "%kO=%f" // mcastDropRate
15057 "%kO=%i" // maxDropCount
15058 "}"
15059 "%kO=%s" // startTime
15060 "%kO=%s" // endTime
15061 "%kO=%lli" // browseTimeSecs
15062 "%kO=%s" // serviceType
15063 "%kO=%b" // flushedCache
15064 #if( MDNSRESPONDER_PROJECT )
15065 "%kO=%b" // usedNewGAI
15066 #endif
15067 "%kO=[%@]" // unexpectedInstances
15068 "%kO=[%@]" // missingInstances
15069 "%kO=[%@]" // incorrectInstances
15070 "}",
15071 kMDNSDiscoveryTestResultsKey_ReplierInfo,
15072 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
15073 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
15074 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
15075 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
15076 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
15077 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
15078 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
15079 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
15080 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
15081 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
15082 kMDNSDiscoveryTestResultsKey_StartTime, startTime,
15083 kMDNSDiscoveryTestResultsKey_EndTime, endTime,
15084 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
15085 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
15086 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
15087 #if( MDNSRESPONDER_PROJECT )
15088 kMDNSDiscoveryTestResultsKey_UsedNewGAI, context->useNewGAI,
15089 #endif
15090 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
15091 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
15092 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
15093 require_noerr( err, exit );
15094
15095 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
15096 require_action( domain, exit, err = kInternalErr );
15097
15098 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
15099 require_action( type, exit, err = kInternalErr );
15100
15101 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
15102 require_action( instanceArray, exit, err = kNoMemoryErr );
15103
15104 hostnameLen = strlen( context->hostname );
15105 for( instance = type->instanceList; instance; instance = instance->next )
15106 {
15107 unsigned int instanceNumber = 0;
15108
15109 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
15110 {
15111 ptr = &instance->name[ hostnameLen ];
15112 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
15113 {
15114 ptr += 2;
15115 for( end = ptr; isdigit_safe( *end ); ++end ) {}
15116 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
15117 {
15118 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
15119 {
15120 instanceNumber = u32;
15121 }
15122 }
15123 }
15124 else if( *ptr == '\0' )
15125 {
15126 instanceNumber = 1;
15127 }
15128 }
15129 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
15130 {
15131 check( !instanceArray[ instanceNumber - 1 ] );
15132 instanceArray[ instanceNumber - 1 ] = instance;
15133 }
15134 else
15135 {
15136 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
15137 "{"
15138 "%kO=%s"
15139 "%kO=%lli"
15140 "}",
15141 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
15142 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
15143 require_noerr( err, exit );
15144 }
15145 }
15146
15147 maxResolveTimeUs = 0;
15148 for( i = 1; i <= context->instanceCount; ++i )
15149 {
15150 int isHostnameValid;
15151 int isTXTValid;
15152
15153 instance = instanceArray[ i - 1 ];
15154 if( !instance )
15155 {
15156 if( i == 1 )
15157 {
15158 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
15159 require_noerr( err, exit );
15160 }
15161 else
15162 {
15163 char * instanceName = NULL;
15164
15165 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
15166 require_action( instanceName, exit, err = kUnknownErr );
15167
15168 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
15169 free( instanceName );
15170 require_noerr( err, exit );
15171 }
15172 continue;
15173 }
15174
15175 if( !instance->hostname )
15176 {
15177 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
15178 "{"
15179 "%kO=%s"
15180 "%kO=%b"
15181 "}",
15182 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
15183 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
15184 require_noerr( err, exit );
15185 continue;
15186 }
15187
15188 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
15189 require_action( badDict, exit, err = kNoMemoryErr );
15190
15191 isHostnameValid = false;
15192 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
15193 {
15194 ptr = &instance->hostname[ hostnameLen ];
15195 if( i == 1 )
15196 {
15197 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
15198 }
15199 else if( *ptr == '-' )
15200 {
15201 ++ptr;
15202 for( end = ptr; isdigit_safe( *end ); ++end ) {}
15203 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
15204 {
15205 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
15206 }
15207 }
15208 }
15209 if( !isHostnameValid )
15210 {
15211 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
15212 kSizeCString );
15213 require_noerr( err, exit );
15214 }
15215
15216 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
15217 {
15218 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
15219 require_noerr( err, exit );
15220 }
15221
15222 isTXTValid = false;
15223 if( instance->txtLen == context->txtSize )
15224 {
15225 uint8_t name[ kDomainNameLengthMax ];
15226
15227 err = DomainNameFromString( name, instance->name, NULL );
15228 require_noerr( err, exit );
15229
15230 err = DomainNameAppendString( name, type->name, NULL );
15231 require_noerr( err, exit );
15232
15233 err = DomainNameAppendString( name, "local", NULL );
15234 require_noerr( err, exit );
15235
15236 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
15237 }
15238 if( !isTXTValid )
15239 {
15240 char * hexStr = NULL;
15241
15242 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
15243 require_action( hexStr, exit, err = kUnknownErr );
15244
15245 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
15246 free( hexStr );
15247 require_noerr( err, exit );
15248 }
15249
15250 if( isHostnameValid )
15251 {
15252 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
15253 unsigned int j;
15254 uint8_t addrV4[ 4 ];
15255 uint8_t addrV6[ 16 ];
15256 uint8_t addrV6LL[ 16 ];
15257
15258 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
15259 else addrV4Bitmap = ~UINT64_C( 0 );
15260
15261 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
15262 else addrV6Bitmap = ~UINT64_C( 0 );
15263
15264 addrV4[ 0 ] = 0;
15265 WriteBig16( &addrV4[ 1 ], i );
15266 addrV4[ 3 ] = 0;
15267
15268 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
15269 WriteBig16( &addrV6[ 12 ], i );
15270
15271 memcpy( addrV6LL, kMDNSReplierLinkLocalBaseAddrV6, 16 );
15272 WriteBig16( &addrV6LL[ 12 ], i );
15273
15274 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15275 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
15276
15277 resolveTimeUs = 0;
15278 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
15279 {
15280 const uint8_t * addrPtr;
15281 unsigned int lsb;
15282 int isAddrValid = false;
15283
15284 if( ipaddr->sip.sa.sa_family == AF_INET )
15285 {
15286 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
15287 lsb = addrPtr[ 3 ];
15288 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
15289 {
15290 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
15291 addrV4Bitmap &= ~bitmask;
15292 isAddrValid = true;
15293 }
15294 }
15295 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
15296 {
15297 const struct sockaddr_in6 * const sin6 = &ipaddr->sip.v6;
15298
15299 addrPtr = sin6->sin6_addr.s6_addr;
15300 lsb = addrPtr[ 15 ];
15301 if( ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
15302 {
15303 const uint32_t scopeID = ( lsb == 1 ) ? context->ifIndex : 0;
15304
15305 if( ( memcmp( addrPtr, ( lsb == 1 ) ? addrV6LL : addrV6, 15 ) == 0 ) &&
15306 ( sin6->sin6_scope_id == scopeID ) )
15307 {
15308 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
15309 addrV6Bitmap &= ~bitmask;
15310 isAddrValid = true;
15311 }
15312 }
15313 }
15314 if( isAddrValid )
15315 {
15316 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
15317 }
15318 else
15319 {
15320 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
15321 require_noerr( err, exit );
15322 }
15323 }
15324
15325 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
15326 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
15327
15328 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
15329 {
15330 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
15331 }
15332 ForgetCF( &unexpectedAddrs );
15333
15334 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15335 require_action( missingAddrs, exit, err = kNoMemoryErr );
15336
15337 for( j = 1; addrV4Bitmap != 0; ++j )
15338 {
15339 bitmask = UINT64_C( 1 ) << ( j - 1 );
15340 if( addrV4Bitmap & bitmask )
15341 {
15342 addrV4Bitmap &= ~bitmask;
15343 addrV4[ 3 ] = (uint8_t) j;
15344 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
15345 require_noerr( err, exit );
15346 }
15347 }
15348 for( j = 1; addrV6Bitmap != 0; ++j )
15349 {
15350 bitmask = UINT64_C( 1 ) << ( j - 1 );
15351 if( addrV6Bitmap & bitmask )
15352 {
15353 struct sockaddr_in6 sin6;
15354 uint8_t missingIPv6[ 16 ];
15355
15356 addrV6Bitmap &= ~bitmask;
15357 memcpy( missingIPv6, ( j == 1 ) ? addrV6LL : addrV6, 16 );
15358 missingIPv6[ 15 ] = (uint8_t) j;
15359 _SockAddrInitIPv6( &sin6, missingIPv6, ( j == 1 ) ? context->ifIndex : 0, 0 );
15360 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%##a", &sin6 );
15361 require_noerr( err, exit );
15362 }
15363 }
15364
15365 if( CFArrayGetCount( missingAddrs ) > 0 )
15366 {
15367 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
15368 }
15369 ForgetCF( &missingAddrs );
15370 }
15371
15372 if( CFDictionaryGetCount( badDict ) > 0 )
15373 {
15374 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
15375 kSizeCString );
15376 require_noerr( err, exit );
15377
15378 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
15379 CFArrayAppendValue( incorrectInstances, badDict );
15380 }
15381 ForgetCF( &badDict );
15382 }
15383
15384 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
15385 ( CFArrayGetCount( missingInstances ) == 0 ) &&
15386 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
15387 {
15388 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
15389 require_noerr( err, exit );
15390 success = true;
15391 }
15392 else
15393 {
15394 success = false;
15395 }
15396 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
15397
15398 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15399 require_noerr_quiet( err, exit );
15400
15401 exit:
15402 ForgetCF( &context->browser );
15403 if( context->replierPID != -1 )
15404 {
15405 kill( context->replierPID, SIGTERM );
15406 context->replierPID = -1;
15407 }
15408 FreeNullSafe( instanceArray );
15409 CFReleaseNullSafe( plist );
15410 CFReleaseNullSafe( badDict );
15411 CFReleaseNullSafe( unexpectedAddrs );
15412 CFReleaseNullSafe( missingAddrs );
15413 exit( err ? 1 : ( success ? 0 : 2 ) );
15414 }
15415
15416 //===========================================================================================================================
15417 // _MDNSDiscoveryTestTXTRecordIsValid
15418 //===========================================================================================================================
15419
15420 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
15421 {
15422 uint32_t hash;
15423 int n;
15424 const uint8_t * ptr;
15425 size_t i, wholeCount, remCount;
15426 uint8_t txtStr[ 16 ];
15427
15428 if( inTXTLen == 0 ) return( false );
15429
15430 hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
15431
15432 txtStr[ 0 ] = 15;
15433 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
15434 check( n == 15 );
15435
15436 ptr = inTXTPtr;
15437 wholeCount = inTXTLen / 16;
15438 for( i = 0; i < wholeCount; ++i )
15439 {
15440 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
15441 ptr += 16;
15442 }
15443
15444 remCount = inTXTLen % 16;
15445 if( remCount > 0 )
15446 {
15447 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
15448 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
15449 ptr += remCount;
15450 }
15451 check( ptr == &inTXTPtr[ inTXTLen ] );
15452 return( true );
15453 }
15454
15455 //===========================================================================================================================
15456 // DotLocalTestCmd
15457 //===========================================================================================================================
15458
15459 #define kDotLocalTestPreparationTimeLimitSecs 5
15460 #define kDotLocalTestSubtestDurationSecs 5
15461
15462 // Constants for SRV record query subtest.
15463
15464 #define kDotLocalTestSRV_Priority 1
15465 #define kDotLocalTestSRV_Weight 0
15466 #define kDotLocalTestSRV_Port 80
15467 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
15468 #define kDotLocalTestSRV_TargetStr "www.example.com."
15469 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
15470
15471 typedef enum
15472 {
15473 kDotLocalTestState_Unset = 0,
15474 kDotLocalTestState_Preparing = 1,
15475 kDotLocalTestState_GAIMDNSOnly = 2,
15476 kDotLocalTestState_GAIDNSOnly = 3,
15477 kDotLocalTestState_GAIBoth = 4,
15478 kDotLocalTestState_GAINeither = 5,
15479 kDotLocalTestState_GAINoSuchRecord = 6,
15480 kDotLocalTestState_QuerySRV = 7,
15481 kDotLocalTestState_Done = 8
15482
15483 } DotLocalTestState;
15484
15485 typedef struct
15486 {
15487 const char * testDesc; // Description of the current subtest.
15488 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
15489 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
15490 NanoTime64 startTime; // Timestamp of when the subtest started.
15491 NanoTime64 endTime; // Timestamp of when the subtest ended.
15492 CFMutableArrayRef correctResults; // Operation results that were expected.
15493 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
15494 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
15495 OSStatus error; // Subtest's error code.
15496 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
15497 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
15498 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
15499 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
15500 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
15501 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
15502 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
15503 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
15504 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
15505 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
15506 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
15507 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
15508 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
15509
15510 } DotLocalSubtest;
15511
15512 typedef struct
15513 {
15514 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
15515 DotLocalSubtest * subtest; // Current subtest's state.
15516 DNSServiceRef connection; // Shared connection for DNS-SD operations.
15517 DNSServiceRef op; // Reference for the current DNS-SD operation.
15518 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
15519 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
15520 char * replierCmd; // Command used to invoke the mdnsreplier.
15521 char * serverCmd; // Command used to invoke the test DNS server.
15522 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
15523 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
15524 NanoTime64 startTime; // Timestamp for when the test started.
15525 NanoTime64 endTime; // Timestamp for when the test ended.
15526 DotLocalTestState state; // The test's current state.
15527 pid_t replierPID; // PID of spawned mdnsreplier.
15528 pid_t serverPID; // PID of spawned test DNS server.
15529 uint32_t ifIndex; // Interface index used for mdnsreplier.
15530 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
15531 OutputFormatType outputFormat; // Format of test results output.
15532 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
15533 Boolean serverIsReady; // True if response was received for test DNS server probe query.
15534 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
15535 Boolean testFailed; // True if at least one subtest failed.
15536 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
15537 // The format of this string is "dotlocal-test-<six random chars>".
15538 } DotLocalTestContext;
15539
15540 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
15541 static void DNSSD_API
15542 _DotLocalTestProbeQueryRecordCallback(
15543 DNSServiceRef inSDRef,
15544 DNSServiceFlags inFlags,
15545 uint32_t inInterfaceIndex,
15546 DNSServiceErrorType inError,
15547 const char * inFullName,
15548 uint16_t inType,
15549 uint16_t inClass,
15550 uint16_t inRDataLen,
15551 const void * inRDataPtr,
15552 uint32_t inTTL,
15553 void * inContext );
15554 static void DNSSD_API
15555 _DotLocalTestRegisterRecordCallback(
15556 DNSServiceRef inSDRef,
15557 DNSRecordRef inRecordRef,
15558 DNSServiceFlags inFlags,
15559 DNSServiceErrorType inError,
15560 void * inContext );
15561 static void _DotLocalTestTimerHandler( void *inContext );
15562 static void DNSSD_API
15563 _DotLocalTestGAICallback(
15564 DNSServiceRef inSDRef,
15565 DNSServiceFlags inFlags,
15566 uint32_t inInterfaceIndex,
15567 DNSServiceErrorType inError,
15568 const char * inHostname,
15569 const struct sockaddr * inSockAddr,
15570 uint32_t inTTL,
15571 void * inContext );
15572 static void DNSSD_API
15573 _DotLocalTestQueryRecordCallback(
15574 DNSServiceRef inSDRef,
15575 DNSServiceFlags inFlags,
15576 uint32_t inInterfaceIndex,
15577 DNSServiceErrorType inError,
15578 const char * inFullName,
15579 uint16_t inType,
15580 uint16_t inClass,
15581 uint16_t inRDataLen,
15582 const void * inRDataPtr,
15583 uint32_t inTTL,
15584 void * inContext );
15585
15586 static void DotLocalTestCmd( void )
15587 {
15588 OSStatus err;
15589 DotLocalTestContext * context;
15590 uint8_t * rdataPtr;
15591 size_t rdataLen;
15592 DNSServiceFlags flags;
15593 char queryName[ 64 ];
15594 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
15595
15596 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
15597 require_action( context, exit, err = kNoMemoryErr );
15598
15599 context->startTime = NanoTimeGetCurrent();
15600 context->endTime = kNanoTime_Invalid;
15601
15602 context->state = kDotLocalTestState_Preparing;
15603
15604 if( gDotLocalTest_Interface )
15605 {
15606 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
15607 require_noerr_quiet( err, exit );
15608 }
15609 else
15610 {
15611 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
15612 require_noerr_quiet( err, exit );
15613 }
15614
15615 if( gDotLocalTest_OutputFilePath )
15616 {
15617 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
15618 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
15619 }
15620
15621 err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
15622 require_noerr_quiet( err, exit );
15623
15624 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15625 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
15626
15627 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15628 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
15629
15630 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
15631 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
15632
15633 // Spawn an mdnsreplier.
15634
15635 ASPrintF( &context->replierCmd,
15636 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
15637 " --countAAAA 1",
15638 (int64_t) getpid(), context->ifIndex, context->labelStr,
15639 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
15640 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
15641
15642 err = _SpawnCommand( &context->replierPID, NULL, NULL, "%s", context->replierCmd );
15643 require_noerr( err, exit );
15644
15645 // Spawn a test DNS server
15646
15647 ASPrintF( &context->serverCmd,
15648 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
15649 (int64_t) getpid(), context->labelStr );
15650 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
15651
15652 err = _SpawnCommand( &context->serverPID, NULL, NULL, "%s", context->serverCmd );
15653 require_noerr( err, exit );
15654
15655 // Create a shared DNS-SD connection.
15656
15657 err = DNSServiceCreateConnection( &context->connection );
15658 require_noerr( err, exit );
15659
15660 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
15661 require_noerr( err, exit );
15662
15663 // Create probe query for DNS server, i.e., query for any name that has an A record.
15664
15665 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
15666
15667 flags = kDNSServiceFlagsShareConnection;
15668 #if( TARGET_OS_WATCH )
15669 flags |= kDNSServiceFlagsPathEvaluationDone;
15670 #endif
15671
15672 context->op = context->connection;
15673 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
15674 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
15675 require_noerr( err, exit );
15676
15677 // Create probe query for mdnsreplier's "about" TXT record.
15678
15679 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
15680
15681 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
15682 #if( TARGET_OS_WATCH )
15683 flags |= kDNSServiceFlagsPathEvaluationDone;
15684 #endif
15685
15686 context->op2 = context->connection;
15687 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
15688 _DotLocalTestProbeQueryRecordCallback, context );
15689 require_noerr( err, exit );
15690
15691 // Register a dummy local. SOA record.
15692
15693 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
15694 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
15695 require_noerr( err, exit );
15696
15697 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
15698 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN,
15699 (uint16_t) rdataLen, rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
15700 require_noerr( err, exit );
15701
15702 // Start timer for probe responses and SOA record registration.
15703
15704 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
15705 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15706 _DotLocalTestTimerHandler, context, &context->timer );
15707 require_noerr( err, exit );
15708 dispatch_resume( context->timer );
15709
15710 dispatch_main();
15711
15712 exit:
15713 if( err ) ErrQuit( 1, "error: %#m\n", err );
15714 }
15715
15716 //===========================================================================================================================
15717 // _DotLocalTestStateMachine
15718 //===========================================================================================================================
15719
15720 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
15721 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
15722 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
15723 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
15724 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
15725
15726 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
15727 {
15728 OSStatus err;
15729 DotLocalTestState nextState;
15730
15731 DNSServiceForget( &inContext->op );
15732 DNSServiceForget( &inContext->op2 );
15733 dispatch_source_forget( &inContext->timer );
15734
15735 switch( inContext->state )
15736 {
15737 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
15738 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
15739 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
15740 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
15741 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
15742 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
15743 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
15744 default: err = kStateErr; goto exit;
15745 }
15746
15747 if( inContext->state == kDotLocalTestState_Preparing )
15748 {
15749 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
15750 {
15751 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
15752 YesNoStr( inContext->registeredSOA ),
15753 YesNoStr( inContext->serverIsReady ),
15754 YesNoStr( inContext->replierIsReady ) );
15755 err = kNotPreparedErr;
15756 goto exit;
15757 }
15758 }
15759 else
15760 {
15761 err = _DotLocalTestFinalizeSubtest( inContext );
15762 require_noerr( err, exit );
15763 }
15764
15765 inContext->state = nextState;
15766 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
15767 err = _DotLocalTestStartSubtest( inContext );
15768
15769 exit:
15770 if( err ) ErrQuit( 1, "error: %#m\n", err );
15771 }
15772
15773 //===========================================================================================================================
15774 // _DotLocalSubtestCreate
15775 //===========================================================================================================================
15776
15777 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
15778 {
15779 OSStatus err;
15780 DotLocalSubtest * obj;
15781
15782 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
15783 require_action( obj, exit, err = kNoMemoryErr );
15784
15785 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15786 require_action( obj->correctResults, exit, err = kNoMemoryErr );
15787
15788 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15789 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
15790
15791 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15792 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
15793
15794 *outSubtest = obj;
15795 obj = NULL;
15796 err = kNoErr;
15797
15798 exit:
15799 if( obj ) _DotLocalSubtestFree( obj );
15800 return( err );
15801 }
15802
15803 //===========================================================================================================================
15804 // _DotLocalSubtestFree
15805 //===========================================================================================================================
15806
15807 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
15808 {
15809 ForgetMem( &inSubtest->queryName );
15810 ForgetCF( &inSubtest->correctResults );
15811 ForgetCF( &inSubtest->duplicateResults );
15812 ForgetCF( &inSubtest->unexpectedResults );
15813 free( inSubtest );
15814 }
15815
15816 //===========================================================================================================================
15817 // _DotLocalTestStartSubtest
15818 //===========================================================================================================================
15819
15820 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
15821 {
15822 OSStatus err;
15823 DotLocalSubtest * subtest = NULL;
15824 DNSServiceRef op = NULL;
15825 DNSServiceFlags flags;
15826
15827 err = _DotLocalSubtestCreate( &subtest );
15828 require_noerr( err, exit );
15829
15830 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
15831 {
15832 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
15833 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15834
15835 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
15836 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
15837
15838 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
15839 memcpy( subtest->addrMDNSv6, kMDNSReplierLinkLocalBaseAddrV6, 16 ); // fe80::2:1
15840 subtest->addrMDNSv6[ 13 ] = 2;
15841 subtest->addrMDNSv6[ 15 ] = 1;
15842
15843 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
15844 }
15845
15846 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
15847 {
15848 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
15849 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15850
15851 subtest->hasDNSv4 = subtest->needDNSv4 = true;
15852 subtest->hasDNSv6 = subtest->needDNSv6 = true;
15853
15854 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
15855 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
15856 subtest->addrDNSv6[ 15 ] = 1;
15857
15858 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
15859 }
15860
15861 else if( inContext->state == kDotLocalTestState_GAIBoth )
15862 {
15863 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
15864 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15865
15866 subtest->hasDNSv4 = subtest->needDNSv4 = true;
15867 subtest->hasDNSv6 = subtest->needDNSv6 = true;
15868 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
15869 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
15870
15871 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
15872 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
15873 subtest->addrDNSv6[ 15 ] = 1;
15874
15875 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
15876 memcpy( subtest->addrMDNSv6, kMDNSReplierLinkLocalBaseAddrV6, 16 ); // fe80::1:1
15877 subtest->addrMDNSv6[ 13 ] = 1;
15878 subtest->addrMDNSv6[ 15 ] = 1;
15879
15880 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
15881 }
15882
15883 else if( inContext->state == kDotLocalTestState_GAINeither )
15884 {
15885 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
15886 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15887
15888 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
15889 }
15890
15891 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
15892 {
15893 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
15894 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15895
15896 subtest->hasDNSv4 = subtest->needDNSv4 = true;
15897 subtest->hasDNSv6 = subtest->needDNSv6 = true;
15898 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
15899 }
15900
15901 else if( inContext->state == kDotLocalTestState_QuerySRV )
15902 {
15903 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
15904 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
15905 inContext->labelStr );
15906 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15907
15908 subtest->needSRV = true;
15909 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
15910 }
15911
15912 else
15913 {
15914 err = kStateErr;
15915 goto exit;
15916 }
15917
15918 // Start new operation.
15919
15920 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
15921 #if( TARGET_OS_WATCH )
15922 flags |= kDNSServiceFlagsPathEvaluationDone;
15923 #endif
15924
15925 subtest->startTime = NanoTimeGetCurrent();
15926 subtest->endTime = kNanoTime_Invalid;
15927
15928 if( inContext->state == kDotLocalTestState_QuerySRV )
15929 {
15930 op = inContext->connection;
15931 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
15932 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
15933 require_noerr( err, exit );
15934 }
15935 else
15936 {
15937 op = inContext->connection;
15938 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
15939 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
15940 require_noerr( err, exit );
15941 }
15942
15943 // Start timer.
15944
15945 check( !inContext->timer );
15946 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
15947 INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15948 _DotLocalTestTimerHandler, inContext, &inContext->timer );
15949 require_noerr( err, exit );
15950 dispatch_resume( inContext->timer );
15951
15952 check( !inContext->op );
15953 inContext->op = op;
15954 op = NULL;
15955
15956 check( !inContext->subtest );
15957 inContext->subtest = subtest;
15958 subtest = NULL;
15959
15960 exit:
15961 if( subtest ) _DotLocalSubtestFree( subtest );
15962 if( op ) DNSServiceRefDeallocate( op );
15963 return( err );
15964 }
15965
15966 //===========================================================================================================================
15967 // _DotLocalTestFinalizeSubtest
15968 //===========================================================================================================================
15969
15970 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
15971 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
15972 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
15973 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
15974 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
15975 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
15976 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
15977 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
15978 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
15979 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
15980 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
15981 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
15982 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
15983 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
15984 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
15985
15986 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
15987 {
15988 OSStatus err;
15989 DotLocalSubtest * subtest;
15990 CFMutableDictionaryRef reportDict;
15991 CFMutableDictionaryRef resultsDict;
15992 CFMutableArrayRef missingResults, reportArray;
15993 char startTime[ 32 ];
15994 char endTime[ 32 ];
15995
15996 subtest = inContext->subtest;
15997 inContext->subtest = NULL;
15998
15999 subtest->endTime = NanoTimeGetCurrent();
16000 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16001 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
16002
16003 reportDict = NULL;
16004 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
16005 "{"
16006 "%kO=%s" // startTime
16007 "%kO=%s" // endTime
16008 "%kO=%s" // queryName
16009 "%kO=%s" // description
16010 "%kO={%@}" // results
16011 "}",
16012 kDotLocalTestReportKey_StartTime, startTime,
16013 kDotLocalTestReportKey_EndTime, endTime,
16014 kDotLocalTestReportKey_QueryName, subtest->queryName,
16015 kDotLocalTestReportKey_Description, subtest->testDesc,
16016 kDotLocalTestReportKey_Results, &resultsDict );
16017 require_noerr( err, exit );
16018
16019 missingResults = NULL;
16020 switch( inContext->state )
16021 {
16022 case kDotLocalTestState_GAIMDNSOnly:
16023 case kDotLocalTestState_GAIDNSOnly:
16024 case kDotLocalTestState_GAIBoth:
16025 case kDotLocalTestState_GAINeither:
16026 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
16027 {
16028 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16029 "["
16030 "%.4a" // Expected DNS IPv4 address
16031 "%.16a" // Expected DNS IPv6 address
16032 "%.4a" // Expected MDNS IPv4 address
16033 "%.16a" // Expected MDNS IPv6 address
16034 "]",
16035 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
16036 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
16037 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
16038 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
16039 require_noerr( err, exit );
16040 }
16041 break;
16042
16043 case kDotLocalTestState_QuerySRV:
16044 if( subtest->needSRV )
16045 {
16046 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16047 "["
16048 "%s" // Expected SRV record data as a string.
16049 "]",
16050 kDotLocalTestSRV_ResultStr );
16051 require_noerr( err, exit );
16052 }
16053 break;
16054
16055 case kDotLocalTestState_GAINoSuchRecord:
16056 if( subtest->needDNSv4 || subtest->needDNSv6 )
16057 {
16058 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16059 "["
16060 "%s" // No Such Record (A)
16061 "%s" // No Such Record (AAAA)
16062 "]",
16063 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
16064 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
16065 require_noerr( err, exit );
16066 }
16067 break;
16068
16069 default:
16070 err = kStateErr;
16071 goto exit;
16072 }
16073
16074 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
16075
16076 if( missingResults )
16077 {
16078 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
16079 ForgetCF( &missingResults );
16080 if( !subtest->error ) subtest->error = kNotFoundErr;
16081 }
16082
16083 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
16084 {
16085 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
16086 if( !subtest->error ) subtest->error = kUnexpectedErr;
16087 }
16088
16089 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
16090 {
16091 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
16092 if( !subtest->error ) subtest->error = kDuplicateErr;
16093 }
16094
16095 if( subtest->error ) inContext->testFailed = true;
16096 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
16097 require_noerr( err, exit );
16098
16099 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
16100 CFArrayAppendValue( reportArray, reportDict );
16101
16102 exit:
16103 _DotLocalSubtestFree( subtest );
16104 CFReleaseNullSafe( reportDict );
16105 return( err );
16106 }
16107
16108 //===========================================================================================================================
16109 // _DotLocalTestFinalizeAndExit
16110 //===========================================================================================================================
16111
16112 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
16113 {
16114 OSStatus err;
16115 CFPropertyListRef plist;
16116 char timestampStart[ 32 ];
16117 char timestampEnd[ 32 ];
16118
16119 check( !inContext->subtest );
16120 inContext->endTime = NanoTimeGetCurrent();
16121
16122 if( inContext->replierPID != -1 )
16123 {
16124 kill( inContext->replierPID, SIGTERM );
16125 inContext->replierPID = -1;
16126 }
16127 if( inContext->serverPID != -1 )
16128 {
16129 kill( inContext->serverPID, SIGTERM );
16130 inContext->serverPID = -1;
16131 }
16132 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
16133 require_noerr( err, exit );
16134
16135 _NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
16136 _NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
16137
16138 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16139 "{"
16140 "%kO=%s" // startTime
16141 "%kO=%s" // endTime
16142 "%kO=%O" // testsGAI
16143 "%kO=%O" // testsQuerySRV
16144 "%kO=%b" // success
16145 "%kO=%s" // replierCmd
16146 "%kO=%s" // serverCmd
16147 "}",
16148 kDotLocalTestReportKey_StartTime, timestampStart,
16149 kDotLocalTestReportKey_EndTime, timestampEnd,
16150 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
16151 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
16152 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
16153 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
16154 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
16155 require_noerr( err, exit );
16156
16157 ForgetCF( &inContext->reportsGAI );
16158 ForgetCF( &inContext->reportsQuerySRV );
16159
16160 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
16161 CFRelease( plist );
16162 require_noerr( err, exit );
16163
16164 exit( inContext->testFailed ? 2 : 0 );
16165
16166 exit:
16167 ErrQuit( 1, "error: %#m\n", err );
16168 }
16169
16170 //===========================================================================================================================
16171 // _DotLocalTestProbeQueryRecordCallback
16172 //===========================================================================================================================
16173
16174 static void DNSSD_API
16175 _DotLocalTestProbeQueryRecordCallback(
16176 DNSServiceRef inSDRef,
16177 DNSServiceFlags inFlags,
16178 uint32_t inInterfaceIndex,
16179 DNSServiceErrorType inError,
16180 const char * inFullName,
16181 uint16_t inType,
16182 uint16_t inClass,
16183 uint16_t inRDataLen,
16184 const void * inRDataPtr,
16185 uint32_t inTTL,
16186 void * inContext )
16187 {
16188 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
16189
16190 Unused( inInterfaceIndex );
16191 Unused( inFullName );
16192 Unused( inType );
16193 Unused( inClass );
16194 Unused( inRDataLen );
16195 Unused( inRDataPtr );
16196 Unused( inTTL );
16197
16198 check( context->state == kDotLocalTestState_Preparing );
16199
16200 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
16201
16202 if( inSDRef == context->op )
16203 {
16204 DNSServiceForget( &context->op );
16205 context->serverIsReady = true;
16206 }
16207 else if( inSDRef == context->op2 )
16208 {
16209 DNSServiceForget( &context->op2 );
16210 context->replierIsReady = true;
16211 }
16212
16213 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
16214 {
16215 _DotLocalTestStateMachine( context );
16216 }
16217
16218 exit:
16219 return;
16220 }
16221
16222 //===========================================================================================================================
16223 // _DotLocalTestRegisterRecordCallback
16224 //===========================================================================================================================
16225
16226 static void DNSSD_API
16227 _DotLocalTestRegisterRecordCallback(
16228 DNSServiceRef inSDRef,
16229 DNSRecordRef inRecordRef,
16230 DNSServiceFlags inFlags,
16231 DNSServiceErrorType inError,
16232 void * inContext )
16233 {
16234 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
16235
16236 Unused( inSDRef );
16237 Unused( inRecordRef );
16238 Unused( inFlags );
16239
16240 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
16241
16242 if( !context->registeredSOA )
16243 {
16244 context->registeredSOA = true;
16245 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
16246 }
16247 }
16248
16249 //===========================================================================================================================
16250 // _DotLocalTestTimerHandler
16251 //===========================================================================================================================
16252
16253 static void _DotLocalTestTimerHandler( void *inContext )
16254 {
16255 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
16256 }
16257
16258 //===========================================================================================================================
16259 // _DotLocalTestGAICallback
16260 //===========================================================================================================================
16261
16262 static void DNSSD_API
16263 _DotLocalTestGAICallback(
16264 DNSServiceRef inSDRef,
16265 DNSServiceFlags inFlags,
16266 uint32_t inInterfaceIndex,
16267 DNSServiceErrorType inError,
16268 const char * inHostname,
16269 const struct sockaddr * inSockAddr,
16270 uint32_t inTTL,
16271 void * inContext )
16272 {
16273 OSStatus err;
16274 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
16275 DotLocalSubtest * const subtest = context->subtest;
16276 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
16277
16278 Unused( inSDRef );
16279 Unused( inInterfaceIndex );
16280 Unused( inHostname );
16281 Unused( inTTL );
16282
16283 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
16284 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
16285
16286 if( context->state == kDotLocalTestState_GAINoSuchRecord )
16287 {
16288 if( inError == kDNSServiceErr_NoSuchRecord )
16289 {
16290 CFMutableArrayRef array = NULL;
16291 const char * noSuchRecordStr;
16292
16293 if( sip->sa.sa_family == AF_INET )
16294 {
16295 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16296 subtest->needDNSv4 = false;
16297
16298 noSuchRecordStr = kNoSuchRecordAStr;
16299 }
16300 else
16301 {
16302 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16303 subtest->needDNSv6 = false;
16304
16305 noSuchRecordStr = kNoSuchRecordAAAAStr;
16306 }
16307 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
16308 require_noerr( err, fatal );
16309 }
16310 else if( !inError )
16311 {
16312 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
16313 require_noerr( err, fatal );
16314 }
16315 else
16316 {
16317 err = inError;
16318 goto exit;
16319 }
16320 }
16321 else
16322 {
16323 if( !inError )
16324 {
16325 CFMutableArrayRef array = NULL;
16326
16327 if( sip->sa.sa_family == AF_INET )
16328 {
16329 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
16330
16331 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
16332 {
16333 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16334 subtest->needDNSv4 = false;
16335 }
16336 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
16337 {
16338 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16339 subtest->needMDNSv4 = false;
16340 }
16341 }
16342 else
16343 {
16344 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
16345
16346 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
16347 {
16348 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16349 subtest->needDNSv6 = false;
16350 }
16351 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
16352 {
16353 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16354 subtest->needMDNSv6 = false;
16355 }
16356 }
16357 if( !array ) array = subtest->unexpectedResults;
16358 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
16359 require_noerr( err, fatal );
16360 }
16361 else if( inError == kDNSServiceErr_NoSuchRecord )
16362 {
16363 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
16364 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
16365 require_noerr( err, fatal );
16366 }
16367 else
16368 {
16369 err = inError;
16370 goto exit;
16371 }
16372 }
16373
16374 exit:
16375 if( err )
16376 {
16377 subtest->error = err;
16378 _DotLocalTestStateMachine( context );
16379 }
16380 return;
16381
16382 fatal:
16383 ErrQuit( 1, "error: %#m\n", err );
16384 }
16385
16386 //===========================================================================================================================
16387 // _DotLocalTestQueryRecordCallback
16388 //===========================================================================================================================
16389
16390 static void DNSSD_API
16391 _DotLocalTestQueryRecordCallback(
16392 DNSServiceRef inSDRef,
16393 DNSServiceFlags inFlags,
16394 uint32_t inInterfaceIndex,
16395 DNSServiceErrorType inError,
16396 const char * inFullName,
16397 uint16_t inType,
16398 uint16_t inClass,
16399 uint16_t inRDataLen,
16400 const void * inRDataPtr,
16401 uint32_t inTTL,
16402 void * inContext )
16403 {
16404 OSStatus err;
16405 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
16406 DotLocalSubtest * const subtest = context->subtest;
16407 const dns_fixed_fields_srv * fields;
16408 const uint8_t * target;
16409 const uint8_t * ptr;
16410 const uint8_t * end;
16411 char * rdataStr;
16412 unsigned int priority, weight, port;
16413 CFMutableArrayRef array;
16414
16415 Unused( inSDRef );
16416 Unused( inInterfaceIndex );
16417 Unused( inFullName );
16418 Unused( inTTL );
16419
16420 check( context->state == kDotLocalTestState_QuerySRV );
16421
16422 err = inError;
16423 require_noerr_quiet( err, exit );
16424 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
16425 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
16426 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
16427
16428 fields = (const dns_fixed_fields_srv *) inRDataPtr;
16429 priority = dns_fixed_fields_srv_get_priority( fields );
16430 weight = dns_fixed_fields_srv_get_weight( fields );
16431 port = dns_fixed_fields_srv_get_port( fields );
16432 target = (const uint8_t *) &fields[ 1 ];
16433 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
16434 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
16435
16436 if( ( priority == kDotLocalTestSRV_Priority ) &&
16437 ( weight == kDotLocalTestSRV_Weight ) &&
16438 ( port == kDotLocalTestSRV_Port ) &&
16439 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
16440 {
16441 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
16442 subtest->needSRV = false;
16443 }
16444 else
16445 {
16446 array = subtest->unexpectedResults;
16447 }
16448
16449 rdataStr = NULL;
16450 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, &rdataStr );
16451 if( !rdataStr )
16452 {
16453 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16454 require_action( rdataStr, fatal, err = kNoMemoryErr );
16455 }
16456
16457 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
16458 free( rdataStr );
16459 require_noerr( err, fatal );
16460
16461 exit:
16462 if( err )
16463 {
16464 subtest->error = err;
16465 _DotLocalTestStateMachine( context );
16466 }
16467 return;
16468
16469 fatal:
16470 ErrQuit( 1, "error: %#m\n", err );
16471 }
16472
16473 //===========================================================================================================================
16474 // ProbeConflictTestCmd
16475 //===========================================================================================================================
16476
16477 #define kProbeConflictTestService_DefaultName "pctest-name"
16478 #define kProbeConflictTestService_Port 60000
16479
16480 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
16481 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
16482
16483 typedef struct
16484 {
16485 const char * description;
16486 const char * program;
16487 Boolean expectsRename;
16488
16489 } ProbeConflictTestCase;
16490
16491 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
16492 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
16493 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
16494
16495 static const ProbeConflictTestCase kProbeConflictTestCases[] =
16496 {
16497 // No conflicts
16498
16499 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
16500
16501 // One multicast probe conflict
16502
16503 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
16504 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
16505 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
16506
16507 // One unicast probe conflict
16508
16509 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
16510 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
16511 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
16512
16513 // One multicast and one unicast probe conflict
16514
16515 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
16516 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
16517 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
16518 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
16519 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
16520 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
16521 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
16522 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
16523 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
16524
16525 // Two multicast probe conflicts
16526
16527 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
16528 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
16529 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
16530 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
16531 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
16532 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
16533 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
16534 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
16535 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
16536 };
16537
16538 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
16539
16540 typedef struct
16541 {
16542 DNSServiceRef registration; // Test service registration.
16543 NanoTime64 testStartTime; // Test's start time.
16544 NanoTime64 startTime; // Current test case's start time.
16545 MDNSColliderRef collider; // mDNS collider object.
16546 CFMutableArrayRef results; // Array of test case results.
16547 char * serviceName; // Test service's instance name as a string. (malloced)
16548 char * serviceType; // Test service's service type as a string. (malloced)
16549 uint8_t * recordName; // FQDN of collider's record (same as test service's records). (malloced)
16550 dispatch_source_t sigSourceINT; // SIGINT signal handler.
16551 dispatch_source_t sigSourceTERM; // SIGTERM signal handler.
16552 CFStringRef exComputerName; // Previous ComputerName.
16553 CFStringRef exLocalHostName;// Previous LocalHostName.
16554 CFStringEncoding exCompNameEnc; // Previous ComputerName's encoding.
16555 unsigned int testCaseIndex; // Index of the current test case.
16556 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
16557 MDNSColliderProtocols protocol; // mDNS collider's IP protocol.
16558 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
16559 OutputFormatType outputFormat; // Format of test report output.
16560 Boolean registered; // True if the test service instance is currently registered.
16561 Boolean testFailed; // True if at least one test case failed.
16562
16563 } ProbeConflictTestContext;
16564
16565 static void DNSSD_API
16566 _ProbeConflictTestRegisterCallback(
16567 DNSServiceRef inSDRef,
16568 DNSServiceFlags inFlags,
16569 DNSServiceErrorType inError,
16570 const char * inName,
16571 const char * inType,
16572 const char * inDomain,
16573 void * inContext );
16574 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
16575 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
16576 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
16577 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
16578 static void _ProbeConflictTestRestoreSystemNames( ProbeConflictTestContext *inContext );
16579 static void _ProbeConflictTestSignalHandler( void *inContext );
16580
16581 static void ProbeConflictTestCmd( void )
16582 {
16583 OSStatus err;
16584 ProbeConflictTestContext * context;
16585 const char * serviceName;
16586 CFStringRef computerName = NULL;
16587 CFStringRef localHostName = NULL;
16588 char * uniqueName;
16589 char tag[ 6 + 1 ];
16590
16591 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
16592 require_action( context, exit, err = kNoMemoryErr );
16593
16594 if( gProbeConflictTest_Interface )
16595 {
16596 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
16597 require_noerr_quiet( err, exit );
16598 }
16599 else
16600 {
16601 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
16602 require_noerr_quiet( err, exit );
16603 }
16604
16605 if( gProbeConflictTest_UseIPv6 )
16606 {
16607 if( gProbeConflictTest_UseIPv4 )
16608 {
16609 FPrintF( stderr, "error: --ipv4 and --ipv6 are mutually exclusive options.\n" );
16610 goto exit;
16611 }
16612 context->protocol = kMDNSColliderProtocol_IPv6;
16613 }
16614 else
16615 {
16616 context->protocol = kMDNSColliderProtocol_IPv4;
16617 }
16618
16619 if( gProbeConflictTest_OutputFilePath )
16620 {
16621 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
16622 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
16623 }
16624
16625 err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
16626 require_noerr_quiet( err, exit );
16627
16628 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
16629 require_action( context->results, exit, err = kNoMemoryErr );
16630
16631 context->testStartTime = NanoTimeGetCurrent();
16632
16633 // Set a unique ComputerName.
16634
16635 computerName = SCDynamicStoreCopyComputerName( NULL, &context->exCompNameEnc );
16636 err = map_scerror( computerName );
16637 require_noerr( err, exit );
16638
16639 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag );
16640 ASPrintF( &uniqueName, "dnssdutil-pctest-computer-name-%s", tag );
16641 require_action( uniqueName, exit, err = kNoMemoryErr );
16642
16643 err = _SetComputerNameWithUTF8CString( uniqueName );
16644 ForgetMem( &uniqueName );
16645 require_noerr( err, exit );
16646 context->exComputerName = computerName;
16647 computerName = NULL;
16648
16649 // Set a unique LocalHostName.
16650
16651 localHostName = SCDynamicStoreCopyLocalHostName( NULL );
16652 err = map_scerror( localHostName );
16653 require_noerr( err, exit );
16654
16655 ASPrintF( &uniqueName, "dnssdutil-pctest-local-hostname-%s", tag );
16656 require_action( uniqueName, exit, err = kNoMemoryErr );
16657
16658 err = _SetLocalHostNameWithUTF8CString( uniqueName );
16659 ForgetMem( &uniqueName );
16660 require_noerr( err, exit );
16661 context->exLocalHostName = localHostName;
16662 localHostName = NULL;
16663
16664 // Set up SIGINT signal handler.
16665
16666 signal( SIGINT, SIG_IGN );
16667 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _ProbeConflictTestSignalHandler, context,
16668 &context->sigSourceINT );
16669 require_noerr( err, exit );
16670 dispatch_resume( context->sigSourceINT );
16671
16672 // Set up SIGTERM signal handler.
16673
16674 signal( SIGTERM, SIG_IGN );
16675 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _ProbeConflictTestSignalHandler, context,
16676 &context->sigSourceTERM );
16677 require_noerr( err, exit );
16678 dispatch_resume( context->sigSourceTERM );
16679
16680 // Register the test service instance.
16681
16682 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
16683
16684 ASPrintF( &context->serviceType, "_pctest-%s._udp", tag );
16685 require_action( context->serviceType, exit, err = kNoMemoryErr );
16686
16687 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
16688 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
16689 require_noerr( err, exit );
16690
16691 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
16692 require_noerr( err, exit );
16693
16694 dispatch_main();
16695
16696 exit:
16697 CFReleaseNullSafe( computerName );
16698 CFReleaseNullSafe( localHostName );
16699 if( context ) _ProbeConflictTestRestoreSystemNames( context );
16700 ErrQuit( 1, "error: %#m\n", err );
16701 }
16702
16703 //===========================================================================================================================
16704 // _ProbeConflictTestRegisterCallback
16705 //===========================================================================================================================
16706
16707 static void DNSSD_API
16708 _ProbeConflictTestRegisterCallback(
16709 DNSServiceRef inSDRef,
16710 DNSServiceFlags inFlags,
16711 DNSServiceErrorType inError,
16712 const char * inName,
16713 const char * inType,
16714 const char * inDomain,
16715 void * inContext )
16716 {
16717 OSStatus err;
16718 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
16719
16720 Unused( inSDRef );
16721 Unused( inType );
16722 Unused( inDomain );
16723
16724 err = inError;
16725 require_noerr( err, exit );
16726
16727 if( !context->registered )
16728 {
16729 if( inFlags & kDNSServiceFlagsAdd )
16730 {
16731 uint8_t * ptr;
16732 size_t recordNameLen;
16733 unsigned int len;
16734 uint8_t name[ kDomainNameLengthMax ];
16735
16736 context->registered = true;
16737
16738 FreeNullSafe( context->serviceName );
16739 context->serviceName = strdup( inName );
16740 require_action( context->serviceName, exit, err = kNoMemoryErr );
16741
16742 err = DomainNameFromString( name, context->serviceName, NULL );
16743 require_noerr( err, exit );
16744
16745 err = DomainNameAppendString( name, context->serviceType, NULL );
16746 require_noerr( err, exit );
16747
16748 err = DomainNameAppendString( name, "local", NULL );
16749 require_noerr( err, exit );
16750
16751 ForgetMem( &context->recordName );
16752 err = DomainNameDup( name, &context->recordName, &recordNameLen );
16753 require_noerr( err, exit );
16754 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
16755
16756 // Make the first label all caps so that it's easier to spot in system logs.
16757
16758 ptr = context->recordName;
16759 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
16760
16761 err = _ProbeConflictTestStartNextTest( context );
16762 require_noerr( err, exit );
16763 }
16764 }
16765 else
16766 {
16767 if( !( inFlags & kDNSServiceFlagsAdd ) )
16768 {
16769 context->registered = false;
16770 err = _ProbeConflictTestStopCurrentTest( context, true );
16771 require_noerr( err, exit );
16772 }
16773 }
16774 err = kNoErr;
16775
16776 exit:
16777 if( err ) exit( 1 );
16778 }
16779
16780 //===========================================================================================================================
16781 // _ProbeConflictTestColliderStopHandler
16782 //===========================================================================================================================
16783
16784 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
16785 {
16786 OSStatus err;
16787 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
16788
16789 err = inError;
16790 require_noerr_quiet( err, exit );
16791
16792 ForgetCF( &context->collider );
16793
16794 err = _ProbeConflictTestStopCurrentTest( context, false );
16795 require_noerr( err, exit );
16796
16797 err = _ProbeConflictTestStartNextTest( context );
16798 require_noerr( err, exit );
16799
16800 exit:
16801 if( err ) exit( 1 );
16802 }
16803
16804 //===========================================================================================================================
16805 // _ProbeConflictTestStartNextTest
16806 //===========================================================================================================================
16807
16808 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
16809 {
16810 OSStatus err;
16811 const ProbeConflictTestCase * testCase;
16812
16813 check( !inContext->collider );
16814
16815 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
16816 {
16817 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
16818 }
16819 else
16820 {
16821 _ProbeConflictTestFinalizeAndExit( inContext );
16822 }
16823
16824 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
16825 require_noerr( err, exit );
16826
16827 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
16828 require_noerr( err, exit );
16829
16830 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
16831 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
16832 require_noerr( err, exit );
16833
16834 MDNSColliderSetProtocols( inContext->collider, inContext->protocol );
16835 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
16836 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
16837
16838 inContext->startTime = NanoTimeGetCurrent();
16839 err = MDNSColliderStart( inContext->collider );
16840 require_noerr( err, exit );
16841
16842 exit:
16843 return( err );
16844 }
16845
16846 //===========================================================================================================================
16847 // _ProbeConflictTestStopCurrentTest
16848 //===========================================================================================================================
16849
16850 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
16851 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
16852 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
16853 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
16854 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
16855 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
16856
16857 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
16858 {
16859 OSStatus err;
16860 const ProbeConflictTestCase * testCase;
16861 NanoTime64 now;
16862 Boolean passed;
16863 char startTime[ 32 ];
16864 char endTime[ 32 ];
16865
16866 now = NanoTimeGetCurrent();
16867
16868 if( inContext->collider )
16869 {
16870 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
16871 MDNSColliderStop( inContext->collider );
16872 CFRelease( inContext->collider );
16873 inContext->collider = NULL;
16874 }
16875
16876 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
16877 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
16878 if( !passed ) inContext->testFailed = true;
16879
16880 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
16881 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16882
16883 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
16884 "{"
16885 "%kO=%s" // description
16886 "%kO=%b" // expectedRename
16887 "%kO=%s" // startTime
16888 "%kO=%s" // endTime
16889 "%kO=%s" // serviceName
16890 "%kO=%b" // passed
16891 "}",
16892 kProbeConflictTestCaseResultKey_Description, testCase->description,
16893 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
16894 kProbeConflictTestCaseResultKey_StartTime, startTime,
16895 kProbeConflictTestCaseResultKey_EndTime, endTime,
16896 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
16897 kProbeConflictTestCaseResultKey_Passed, passed );
16898 require_noerr( err, exit );
16899
16900 ++inContext->testCaseIndex;
16901
16902 exit:
16903 return( err );
16904 }
16905
16906 //===========================================================================================================================
16907 // _ProbeConflictTestFinalizeAndExit
16908 //===========================================================================================================================
16909
16910 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
16911 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
16912 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
16913 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
16914 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
16915
16916 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
16917 {
16918 OSStatus err;
16919 CFPropertyListRef plist;
16920 NanoTime64 now;
16921 int exitCode;
16922 char startTime[ 32 ];
16923 char endTime[ 32 ];
16924
16925 now = NanoTimeGetCurrent();
16926
16927 check( !inContext->collider );
16928
16929 _NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
16930 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16931
16932 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16933 "{"
16934 "%kO=%s" // startTime
16935 "%kO=%s" // endTime
16936 "%kO=%s" // serviceType
16937 "%kO=%O" // results
16938 "%kO=%b" // passed
16939 "}",
16940 kProbeConflictTestReportKey_StartTime, startTime,
16941 kProbeConflictTestReportKey_EndTime, endTime,
16942 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
16943 kProbeConflictTestReportKey_Results, inContext->results,
16944 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
16945 require_noerr( err, exit );
16946 ForgetCF( &inContext->results );
16947
16948 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
16949 CFRelease( plist );
16950 require_noerr( err, exit );
16951
16952 exit:
16953 _ProbeConflictTestRestoreSystemNames( inContext );
16954 if( err )
16955 {
16956 FPrintF( stderr, "error: %#m\n", err );
16957 exitCode = 1;
16958 }
16959 else
16960 {
16961 exitCode = inContext->testFailed ? 2 : 0;
16962 }
16963 exit( exitCode );
16964 }
16965
16966 //===========================================================================================================================
16967 // _ProbeConflictTestRestoreSystemNames
16968 //===========================================================================================================================
16969
16970 static void _ProbeConflictTestRestoreSystemNames( ProbeConflictTestContext *inContext )
16971 {
16972 OSStatus err;
16973
16974 if( inContext->exComputerName )
16975 {
16976 err = _SetComputerName( inContext->exComputerName, inContext->exCompNameEnc );
16977 check_noerr( err );
16978 ForgetCF( &inContext->exComputerName );
16979 }
16980 if( inContext->exLocalHostName )
16981 {
16982 err = _SetLocalHostName( inContext->exLocalHostName );
16983 check_noerr( err );
16984 ForgetCF( &inContext->exLocalHostName );
16985 }
16986 }
16987
16988 //===========================================================================================================================
16989 // _ProbeConflictTestSignalHandler
16990 //===========================================================================================================================
16991
16992 static void _ProbeConflictTestSignalHandler( void *inContext )
16993 {
16994 _ProbeConflictTestRestoreSystemNames( inContext );
16995 FPrintF( stderr, "Probe conflict test got a SIGINT or SIGTERM signal, exiting...\n" );
16996 exit( 1 );
16997 }
16998
16999 #if( MDNSRESPONDER_PROJECT )
17000 //===========================================================================================================================
17001 // FallbackTestCmd
17002 //===========================================================================================================================
17003
17004 typedef struct
17005 {
17006 unsigned int serverIndex; // Index of server that is soley capable of answering query.
17007
17008 } FallbackSubtestParams;
17009
17010 #define kFallbackTestSubtestCount 8
17011
17012 const FallbackSubtestParams kFallbackSubtestParams[] = { { 2 }, { 4 }, { 1 }, { 3 }, { 2 }, { 1 }, { 4 }, { 3 } };
17013 check_compile_time( countof( kFallbackSubtestParams ) == kFallbackTestSubtestCount );
17014
17015 typedef struct
17016 {
17017 char * hostname; // Hostname to resolve.
17018 NanoTime64 startTime; // Subtest's start time.
17019 NanoTime64 endTime; // Subtest's end time.
17020 OSStatus error; // Subtest's current error.
17021
17022 } FallbackSubtest;
17023
17024 typedef struct
17025 {
17026 dispatch_queue_t queue; // Serial queue for test events.
17027 dispatch_semaphore_t doneSem; // Semaphore to signal when the test is done.
17028 DNSServiceRef gai; // Current DNSServiceGetAddrInfo request.
17029 dispatch_source_t timer; // Timer for enforcing time limit on current DNSServiceGetAddrInfo request.
17030 size_t subtestIndex; // Index of current subtest.
17031 pid_t serverPID; // PID of spawned test DNS server.
17032 OSStatus error; // Current test error.
17033 NanoTime64 startTime; // Test's start time.
17034 NanoTime64 endTime; // Test's end time.
17035 char * serverCmd; // Command used to invoke the test DNS server.
17036 char * probeHostname; // Hostname queried to verify that server is up and running.
17037 FallbackSubtest subtests[ kFallbackTestSubtestCount ];
17038 Boolean useRefused; // True if server uses Refused RCODE for queries it's not allowed to answer.
17039
17040 } FallbackTest;
17041
17042 static OSStatus _FallbackTestCreate( FallbackTest **outTest );
17043 static OSStatus _FallbackTestRun( FallbackTest *inTest );
17044 static void _FallbackTestFree( FallbackTest *inTest );
17045
17046 ulog_define_ex( kDNSSDUtilIdentifier, FallbackTest, kLogLevelInfo, kLogFlags_None, "FallbackTest", NULL );
17047 #define ft_ulog( LEVEL, ... ) ulog( &log_category_from_name( FallbackTest ), (LEVEL), __VA_ARGS__ )
17048
17049 static void FallbackTestCmd( void )
17050 {
17051 OSStatus err;
17052 FallbackTest * test = NULL;
17053 CFPropertyListRef plist = NULL;
17054 OutputFormatType outputFormat;
17055 size_t i;
17056 CFMutableArrayRef results;
17057 Boolean testPassed = false;
17058 Boolean subtestFailed;
17059 char startTime[ 32 ];
17060 char endTime[ 32 ];
17061
17062 err = CheckRootUser();
17063 require_noerr_quiet( err, exit );
17064
17065 err = OutputFormatFromArgString( gFallbackTest_OutputFormat, &outputFormat );
17066 require_noerr_quiet( err, exit );
17067
17068 err = _FallbackTestCreate( &test );
17069 require_noerr( err, exit );
17070
17071 if( gFallbackTest_UseRefused ) test->useRefused = true;
17072 err = _FallbackTestRun( test );
17073 require_noerr( err, exit );
17074
17075 _NanoTime64ToTimestamp( test->startTime, startTime, sizeof( startTime ) );
17076 _NanoTime64ToTimestamp( test->endTime, endTime, sizeof( endTime ) );
17077 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
17078 "{"
17079 "%kO=%s" // startTime
17080 "%kO=%s" // endTime
17081 "%kO=%s" // serverCmd
17082 "%kO=[%@]" // results
17083 "}",
17084 CFSTR( "startTime" ), startTime,
17085 CFSTR( "endTime" ), endTime,
17086 CFSTR( "serverCmd" ), test->serverCmd,
17087 CFSTR( "results" ), &results );
17088 require_noerr( err, exit );
17089
17090 subtestFailed = false;
17091 check( test->subtestIndex == kFallbackTestSubtestCount );
17092 for( i = 0; i < kFallbackTestSubtestCount; ++i )
17093 {
17094 CFMutableDictionaryRef resultDict;
17095 FallbackSubtest * const subtest = &test->subtests[ i ];
17096 char errorDesc[ 128 ];
17097
17098 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results, "{%@}", &resultDict );
17099 require_noerr( err, exit );
17100
17101 err = CFDictionarySetCString( resultDict, CFSTR( "name" ), subtest->hostname, kSizeCString );
17102 require_noerr( err, exit );
17103
17104 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
17105 err = CFDictionarySetCString( resultDict, CFSTR( "startTime" ), startTime, kSizeCString );
17106 require_noerr( err, exit );
17107
17108 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
17109 err = CFDictionarySetCString( resultDict, CFSTR( "endTime" ), endTime, kSizeCString );
17110 require_noerr( err, exit );
17111
17112 SNPrintF( errorDesc, sizeof( errorDesc ), "%m", subtest->error );
17113 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultDict,
17114 "%kO="
17115 "{"
17116 "%kO=%lli" // code
17117 "%kO=%s" // description
17118 "}",
17119 CFSTR( "error" ),
17120 CFSTR( "code" ), (int64_t) subtest->error,
17121 CFSTR( "description" ), errorDesc );
17122 require_noerr( err, exit );
17123
17124 if( subtest->error ) subtestFailed = true;
17125 }
17126 if( !subtestFailed ) testPassed = true;
17127 CFPropertyListAppendFormatted( kCFAllocatorDefault, plist, "%kO=%b", CFSTR( "pass" ), testPassed );
17128
17129 err = OutputPropertyList( plist, outputFormat, gFallbackTest_OutputFilePath );
17130 require_noerr( err, exit );
17131
17132 exit:
17133 if( test ) _FallbackTestFree( test );
17134 CFReleaseNullSafe( plist );
17135 gExitCode = err ? 1 : ( testPassed ? 0 : 2 );
17136 }
17137
17138 //===========================================================================================================================
17139
17140 static void _FallbackTestStart( void *inContext );
17141 static void _FallbackTestStop( FallbackTest *inTest, OSStatus inError );
17142 static void DNSSD_API
17143 _FallbackTestProbeGAICallback(
17144 DNSServiceRef inSDRef,
17145 DNSServiceFlags inFlags,
17146 uint32_t inInterfaceIndex,
17147 DNSServiceErrorType inError,
17148 const char * inHostname,
17149 const struct sockaddr * inSockAddr,
17150 uint32_t inTTL,
17151 void * inContext );
17152 static void _FallbackTestProbeTimerHandler( void *inContext );
17153 static void DNSSD_API
17154 _FallbackTestGetAddrInfoCallback(
17155 DNSServiceRef inSDRef,
17156 DNSServiceFlags inFlags,
17157 uint32_t inInterfaceIndex,
17158 DNSServiceErrorType inError,
17159 const char * inHostname,
17160 const struct sockaddr * inSockAddr,
17161 uint32_t inTTL,
17162 void * inContext );
17163 static void _FallbackTestGAITimerHandler( void *inContext );
17164 static OSStatus _FallbackTestStartSubtest( FallbackTest *inTest );
17165 static void _FallbackTestForgetSources( FallbackTest *inTest );
17166
17167 #define kFallbackTestProbeTimeLimitSecs 5
17168 #define kFallbackTestGAITimeLimitSecs 75
17169
17170 static OSStatus _FallbackTestCreate( FallbackTest **outTest )
17171 {
17172 OSStatus err;
17173 FallbackTest * test;
17174
17175 test = (FallbackTest *) calloc( 1, sizeof( *test ) );
17176 require_action( test, exit, err = kNoMemoryErr );
17177
17178 test->error = kInProgressErr;
17179 test->serverPID = -1;
17180
17181 test->queue = dispatch_queue_create( "com.apple.dnssdutil.fallback-test", DISPATCH_QUEUE_SERIAL );
17182 require_action( test->queue, exit, err = kNoResourcesErr );
17183
17184 test->doneSem = dispatch_semaphore_create( 0 );
17185 require_action( test->doneSem, exit, err = kNoResourcesErr );
17186
17187 *outTest = test;
17188 test = NULL;
17189 err = kNoErr;
17190
17191 exit:
17192 if( test ) _FallbackTestFree( test );
17193 return( err );
17194 }
17195
17196 //===========================================================================================================================
17197
17198 static OSStatus _FallbackTestRun( FallbackTest *inTest )
17199 {
17200 dispatch_async_f( inTest->queue, inTest, _FallbackTestStart );
17201 dispatch_semaphore_wait( inTest->doneSem, DISPATCH_TIME_FOREVER );
17202 return( inTest->error );
17203 }
17204
17205 //===========================================================================================================================
17206
17207 static void _FallbackTestFree( FallbackTest *inTest )
17208 {
17209 size_t i;
17210
17211 check( !inTest->gai );
17212 check( !inTest->timer );
17213 check( inTest->serverPID < 0 );
17214
17215 ForgetMem( &inTest->serverCmd );
17216 ForgetMem( &inTest->probeHostname );
17217 dispatch_forget( &inTest->queue );
17218 dispatch_forget( &inTest->doneSem );
17219 for( i = 0; i < kFallbackTestSubtestCount; ++i )
17220 {
17221 FallbackSubtest * const subtest = &inTest->subtests[ i ];
17222
17223 ForgetMem( &subtest->hostname );
17224 }
17225 free( inTest );
17226 }
17227
17228 //===========================================================================================================================
17229
17230 static void _FallbackTestStart( void *inContext )
17231 {
17232 OSStatus err;
17233 FallbackTest * const test = (FallbackTest *) inContext;
17234 char tag[ 6 + 1 ];
17235
17236 test->startTime = NanoTimeGetCurrent();
17237
17238 // The "dnssdutil server" command will create a resolver entry for the server's "d.test." domain containing an array
17239 // of the server's IP addresses. Because configd favors IPv6 addresses, when there's a mix of IPv4 and IPv6
17240 // addresses, configd may rearrange the array in order to ensure that IPv6 addresses come before the IPv4 addresses.
17241 // To preserve the original address order, the server is specified to run in IPv6-only mode. This way,
17242 // mDNSResponder's view of the address will be such that address with index value 1 is first, address with index
17243 // value 2 is second, etc.
17244
17245 ASPrintF( &test->serverCmd, "dnssdutil server --loopback --follow %lld --ipv6 --extraIPv6 3%s",
17246 (int64_t) getpid(), test->useRefused ? " --useRefused" : "" );
17247 require_action_quiet( test->serverCmd, exit, err = kUnknownErr );
17248
17249 err = _SpawnCommand( &test->serverPID, "/dev/null", "/dev/null", "%s", test->serverCmd );
17250 require_noerr( err, exit );
17251
17252 ASPrintF( &test->probeHostname, "tag-fallback-test-probe-%s.count-1.ipv4.ttl-900.d.test.",
17253 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
17254 require_action( test->probeHostname, exit, err = kNoMemoryErr );
17255
17256 ft_ulog( kLogLevelInfo, "Starting GetAddrInfo request for %s\n", test->probeHostname );
17257
17258 err = DNSServiceGetAddrInfo( &test->gai, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
17259 test->probeHostname, _FallbackTestProbeGAICallback, test );
17260 require_noerr( err, exit );
17261
17262 err = DNSServiceSetDispatchQueue( test->gai, test->queue );
17263 require_noerr( err, exit );
17264
17265 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kFallbackTestProbeTimeLimitSecs ),
17266 kFallbackTestProbeTimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ), test->queue,
17267 _FallbackTestProbeTimerHandler, test, &test->timer );
17268 require_noerr( err, exit );
17269 dispatch_resume( test->timer );
17270
17271 exit:
17272 if( err ) _FallbackTestStop( test, err );
17273 }
17274
17275 //===========================================================================================================================
17276
17277 static void _FallbackTestStop( FallbackTest *inTest, OSStatus inError )
17278 {
17279 inTest->error = inError;
17280 inTest->endTime = NanoTimeGetCurrent();
17281 _FallbackTestForgetSources( inTest );
17282 if( inTest->serverPID >= 0 )
17283 {
17284 OSStatus err;
17285
17286 err = kill( inTest->serverPID, SIGTERM );
17287 err = map_global_noerr_errno( err );
17288 check_noerr( err );
17289 inTest->serverPID = -1;
17290 }
17291 dispatch_semaphore_signal( inTest->doneSem );
17292 }
17293
17294 //===========================================================================================================================
17295
17296 static void DNSSD_API
17297 _FallbackTestProbeGAICallback(
17298 DNSServiceRef inSDRef,
17299 DNSServiceFlags inFlags,
17300 uint32_t inInterfaceIndex,
17301 DNSServiceErrorType inError,
17302 const char * inHostname,
17303 const struct sockaddr * inSockAddr,
17304 uint32_t inTTL,
17305 void * inContext )
17306 {
17307 OSStatus err;
17308 FallbackTest * const test = (FallbackTest *) inContext;
17309
17310 Unused( inSDRef );
17311 Unused( inInterfaceIndex );
17312 Unused( inHostname );
17313 Unused( inTTL );
17314
17315 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
17316 {
17317 _FallbackTestForgetSources( test );
17318
17319 ft_ulog( kLogLevelInfo, "Probe: Got GAI address %##a for %s\n", inSockAddr, test->probeHostname );
17320
17321 check( test->subtestIndex == 0 );
17322 err = _FallbackTestStartSubtest( test );
17323 require_noerr( err, exit );
17324 }
17325 err = kNoErr;
17326
17327 exit:
17328 if( err ) _FallbackTestStop( test, err );
17329 }
17330
17331 //===========================================================================================================================
17332
17333 static void _FallbackTestProbeTimerHandler( void *inContext )
17334 {
17335 FallbackTest * const test = (FallbackTest *) inContext;
17336
17337 ft_ulog( kLogLevelInfo, "GetAddrInfo probe request for \"%s\" timed out.\n", test->probeHostname );
17338 _FallbackTestStop( test, kNotPreparedErr );
17339 }
17340
17341 //===========================================================================================================================
17342
17343 static void DNSSD_API
17344 _FallbackTestGetAddrInfoCallback(
17345 DNSServiceRef inSDRef,
17346 DNSServiceFlags inFlags,
17347 uint32_t inInterfaceIndex,
17348 DNSServiceErrorType inError,
17349 const char * inHostname,
17350 const struct sockaddr * inSockAddr,
17351 uint32_t inTTL,
17352 void * inContext )
17353 {
17354 OSStatus err;
17355 struct sockaddr_in sin;
17356 FallbackTest * const test = (FallbackTest *) inContext;
17357 FallbackSubtest *const subtest = &test->subtests[ test->subtestIndex ];
17358 Boolean complete = false;
17359
17360 Unused( inSDRef );
17361 Unused( inInterfaceIndex );
17362 Unused( inTTL );
17363
17364 _FallbackTestForgetSources( test );
17365
17366 if( strcasecmp( inHostname, subtest->hostname ) != 0 )
17367 {
17368 ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected hostname \"%s\".\n",
17369 subtest->hostname, inHostname );
17370 err = kUnexpectedErr;
17371 goto done;
17372 }
17373 if( inError )
17374 {
17375 ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected error %#m.\n", subtest->hostname, inError );
17376 err = inError;
17377 goto done;
17378 }
17379 if( ( inFlags & kDNSServiceFlagsAdd ) == 0 )
17380 {
17381 ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Missing Add flag.\n", subtest->hostname );
17382 err = kUnexpectedErr;
17383 goto done;
17384 }
17385 _SockAddrInitIPv4( &sin, kDNSServerBaseAddrV4 + 1, 0 );
17386 if( SockAddrCompareAddr( inSockAddr, &sin ) != 0 )
17387 {
17388 ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected address %##a (expected %##a).\n",
17389 subtest->hostname, inSockAddr, &sin );
17390 err = kUnexpectedErr;
17391 goto done;
17392 }
17393 ft_ulog( kLogLevelInfo, "Subtest %zu/%d: Got expected GAI address %##a for %s\n",
17394 test->subtestIndex + 1, kFallbackTestSubtestCount, inSockAddr, subtest->hostname );
17395 err = kNoErr;
17396
17397 done:
17398 subtest->endTime = NanoTimeGetCurrent();
17399 subtest->error = err;
17400 err = kNoErr;
17401 if( ++test->subtestIndex < kFallbackTestSubtestCount )
17402 {
17403 err = _FallbackTestStartSubtest( test );
17404 require_noerr( err, exit );
17405 }
17406 else
17407 {
17408 complete = true;
17409 }
17410
17411 exit:
17412 if( err || complete ) _FallbackTestStop( test, err );
17413 }
17414
17415 //===========================================================================================================================
17416
17417 static void _FallbackTestGAITimerHandler( void *inContext )
17418 {
17419 OSStatus err;
17420 FallbackTest * const test = (FallbackTest *) inContext;
17421 FallbackSubtest * const subtest = &test->subtests[ test->subtestIndex ];
17422 Boolean complete = false;
17423
17424 _FallbackTestForgetSources( test );
17425
17426 ft_ulog( kLogLevelInfo, "GetAddrInfo request for \"%s\" timed out.\n", subtest->hostname );
17427
17428 subtest->endTime = NanoTimeGetCurrent();
17429 subtest->error = kTimeoutErr;
17430 if( ++test->subtestIndex < kFallbackTestSubtestCount )
17431 {
17432 err = _FallbackTestStartSubtest( test );
17433 require_noerr( err, exit );
17434 }
17435 else
17436 {
17437 complete = true;
17438 err = kNoErr;
17439 }
17440
17441 exit:
17442 if( err || complete ) _FallbackTestStop( test, err );
17443 }
17444
17445 //===========================================================================================================================
17446
17447 static OSStatus _FallbackTestStartSubtest( FallbackTest *inTest )
17448 {
17449 OSStatus err;
17450 FallbackSubtest * const subtest = &inTest->subtests[ inTest->subtestIndex ];
17451 char tag[ 6 + 1 ];
17452
17453 subtest->error = kInProgressErr;
17454 subtest->startTime = NanoTimeGetCurrent();
17455
17456 ForgetMem( &subtest->hostname );
17457 ASPrintF( &subtest->hostname, "index-%u.tag-fallback-test-%s.count-1.ipv4.ttl-900.d.test.",
17458 kFallbackSubtestParams[ inTest->subtestIndex ].serverIndex,
17459 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
17460 require_action( subtest->hostname, exit, err = kNoMemoryErr );
17461
17462 ft_ulog( kLogLevelInfo, "Starting GetAddrInfo request for %s\n", subtest->hostname );
17463
17464 check( !inTest->gai );
17465 err = DNSServiceGetAddrInfo( &inTest->gai, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
17466 subtest->hostname, _FallbackTestGetAddrInfoCallback, inTest );
17467 require_noerr( err, exit );
17468
17469 err = DNSServiceSetDispatchQueue( inTest->gai, inTest->queue );
17470 require_noerr( err, exit );
17471
17472 check( !inTest->timer );
17473 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kFallbackTestGAITimeLimitSecs ),
17474 kFallbackTestGAITimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ), inTest->queue,
17475 _FallbackTestGAITimerHandler, inTest, &inTest->timer );
17476 require_noerr( err, exit );
17477 dispatch_resume( inTest->timer );
17478
17479 exit:
17480 return( err );
17481 }
17482
17483 //===========================================================================================================================
17484
17485 static void _FallbackTestForgetSources( FallbackTest *inTest )
17486 {
17487 DNSServiceForget( &inTest->gai );
17488 dispatch_source_forget( &inTest->timer );
17489 }
17490
17491 //===========================================================================================================================
17492 // ExpensiveConstrainedsTestCmd
17493 //===========================================================================================================================
17494
17495 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
17496 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
17497 #define LOOPBACK_INTERFACE_NAME "lo0"
17498 #define WIFI_TEST_QUESTION_NAME "www.example.com"
17499 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
17500 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
17501 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
17502 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
17503
17504 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
17505 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
17506 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
17507 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
17508
17509 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
17510 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
17511 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
17512 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
17513 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
17514 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
17515 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
17516 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
17517 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
17518 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
17519
17520 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
17521 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
17522 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
17523 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
17524 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
17525 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
17526 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
17527 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
17528
17529 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
17530 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
17531 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
17532 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
17533 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
17534
17535 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
17536 enum ExpensiveConstrainedTestState
17537 {
17538 TEST_BEGIN,
17539 TEST_EXPENSIVE_PREPARE,
17540 TEST_EXPENSIVE, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
17541 TEST_CONSTRAINED_PREPARE,
17542 TEST_CONSTRAINED, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
17543 TEST_EXPENSIVE_CONSTRAINED_PREPARE,
17544 TEST_EXPENSIVE_CONSTRAINED, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
17545 TEST_FAILED,
17546 TEST_SUCCEEDED
17547 };
17548 enum ExpensiveConstrainedTestOperation
17549 {
17550 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.
17551 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.
17552 NO_UPDATE // no status update notification
17553 };
17554
17555 typedef struct
17556 {
17557 uint32_t subtestIndex; // The index of parameter for the subtest
17558 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
17559 const char * name; // Hostname to resolve.
17560 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
17561 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
17562 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
17563 char ifName[IFNAMSIZ]; // Interface name for the given interface index.
17564 dispatch_source_t timer; // The test will check if the current behavior is valid, which is called by
17565 // the timer per 2s.
17566 pid_t serverPID;
17567 Boolean isExpensivePrev; // If the interface is expensive in the previous test step.
17568 Boolean isExpensiveNow; // If the interface is expensive now.
17569 Boolean isConstrainedPrev; // If the interface is constrained in the previous test step.
17570 Boolean isConstrainedNow; // If the interface is constrained now.
17571 Boolean startFromExpensive; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
17572 uint8_t numOfRetries; // the number of retries we can have if the test fail
17573 struct timeval updateTime; // The time when interface status(expensive or constrained) is changed.
17574 struct timeval notificationTime; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
17575 uint32_t counter; // To record how many times the test has repeated.
17576 enum ExpensiveConstrainedTestState state; // The current test state.
17577 enum ExpensiveConstrainedTestOperation expectedOperation; // the test expects this kind of notification
17578 enum ExpensiveConstrainedTestOperation operation; // represents what notification the callback function gets.
17579
17580 NanoTime64 testReport_startTime; // when the entire test starts
17581 CFMutableArrayRef subtestReport; // stores the log message for every subtest
17582 NanoTime64 subtestReport_startTime; // when the subtest starts
17583 CFMutableArrayRef subtestProgress; // one test iteration
17584 NanoTime64 subtestProgress_startTime; // when the test iteration starts
17585 CFMutableArrayRef subtestProgress_callBack; // array of ADD/REMOVE events
17586 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
17587 OutputFormatType outputFormat; // Format of test report output.
17588 } ExpensiveConstrainedContext;
17589
17590 // structure that controls how the subtest is run
17591 typedef struct
17592 {
17593 const char *qname; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
17594 Boolean deny_expensive; // if the query should avoid using expensive interface
17595 Boolean deny_constrained; // if the query should avoid using constrained interface
17596 Boolean start_from_expensive; // if the query should starts from using an expensive interface
17597 Boolean ipv4_query; // only allow IPv4 query
17598 Boolean ipv6_query; // only allow IPv6 query
17599 int8_t test_passed; // if the subtest passes
17600 } ExpensiveConstrainedTestParams;
17601
17602 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams[] =
17603 {
17604 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
17605 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
17606 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
17607 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
17608 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
17609 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
17610 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
17611 // IPv4 Only
17612 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
17613 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
17614 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
17615 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
17616 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
17617 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
17618 // IPv6 Only
17619 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
17620 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
17621 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
17622 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
17623 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
17624 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
17625 };
17626
17627 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
17628 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
17629 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
17630 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
17631 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
17632 static void DNSSD_API
17633 ExpensiveConstrainedCallback(
17634 DNSServiceRef inSDRef,
17635 DNSServiceFlags inFlags,
17636 uint32_t inInterfaceIndex,
17637 DNSServiceErrorType inError,
17638 const char * inHostname,
17639 const struct sockaddr * inSockAddr,
17640 uint32_t inTTL,
17641 void * inContext );
17642 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
17643 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
17644 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
17645 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
17646 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
17647 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
17648 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
17649 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
17650 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
17651
17652 //===========================================================================================================================
17653 // ExpensiveConstrainedTestCmd
17654 //===========================================================================================================================
17655
17656 static void ExpensiveConstrainedTestCmd( void )
17657 {
17658 OSStatus err;
17659 dispatch_source_t signalSource = NULL;
17660 ExpensiveConstrainedContext * context = NULL;
17661
17662 // Set up SIGINT handler.
17663 signal( SIGINT, SIG_IGN );
17664 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
17665 require_noerr( err, exit );
17666 dispatch_resume( signalSource );
17667
17668 // create the test context
17669 context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
17670 require_action( context, exit, err = kNoMemoryErr );
17671
17672 // get the command line option
17673 err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
17674 require_noerr_quiet( err, exit );
17675 if ( gExpensiveConstrainedTest_OutputFilePath )
17676 {
17677 context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
17678 require_noerr_quiet( context->outputFilePath, exit );
17679 }
17680
17681 // initialize context
17682 context->subtestIndex = 0;
17683 context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
17684
17685 // initialize the CFArray used to store the log
17686 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17687 context->testReport_startTime = NanoTimeGetCurrent();
17688
17689 // setup local DNS server
17690 ExpensiveConstrainedSetupLocalDNSServer( context );
17691
17692 ExpensiveConstrainedStartTestHandler( context );
17693
17694 dispatch_main();
17695
17696 exit:
17697 exit( 1 );
17698 }
17699
17700 //===========================================================================================================================
17701 // ExpensiveConstrainedSetupLocalDNSServer
17702 //===========================================================================================================================
17703
17704 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
17705 {
17706 pid_t current_pid = getpid();
17707 OSStatus err = _SpawnCommand( &context->serverPID, NULL, NULL, "dnssdutil server -l --port 0 --follow %d", current_pid );
17708 if (err != 0)
17709 {
17710 FPrintF( stdout, "dnssdutil server -l --port 0 --follow <PID> failed, error: %d\n", err );
17711 exit( 1 );
17712 }
17713 sleep(2);
17714 }
17715
17716 //===========================================================================================================================
17717 // ExpensiveConstrainedStartTestHandler
17718 //===========================================================================================================================
17719
17720 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
17721 {
17722 // setup 3s timer
17723 ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
17724
17725 // set the event handler for the 3s timer
17726 dispatch_source_set_event_handler( context->timer, ^{
17727 ExpensiveConstrainedTestTimerEventHandler( context );
17728 } );
17729
17730 dispatch_resume( context->timer );
17731 }
17732
17733 //===========================================================================================================================
17734 // ExpensiveConstrainedStartTestHandler
17735 //===========================================================================================================================
17736
17737 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
17738 {
17739 dispatch_cancel( context->timer );
17740 dispatch_release( context->timer );
17741 context->timer = NULL;
17742 }
17743
17744 //===========================================================================================================================
17745 // ExpensiveConstrainedSetupTimer
17746 //===========================================================================================================================
17747
17748 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
17749 {
17750 // set the timer source, the event handler will be called for every "second" seconds
17751 context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
17752 if ( context->timer == NULL )
17753 {
17754 FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
17755 exit( 1 );
17756 }
17757 // the first block will be put into the queue "second"s after calling dispatch_resume
17758 dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
17759 (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
17760 }
17761
17762 //===========================================================================================================================
17763 // ExpensiveConstrainedTestTimerEventHandler
17764 //===========================================================================================================================
17765
17766 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
17767 {
17768 OSStatus err;
17769 char buffer[ 1024 ];
17770 const char *errorDescription = NULL;
17771
17772 // do not log the state if we are in transition state
17773 if (context->state != TEST_BEGIN
17774 && context->state != TEST_SUCCEEDED
17775 && context->state != TEST_CONSTRAINED_PREPARE
17776 && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
17777 ExpensiveConstrainedSubtestProgressReport( context );
17778
17779 switch ( context->state ) {
17780 case TEST_BEGIN:
17781 {
17782 ExpensiveConstrainedStopTestHandler( context );
17783
17784 // clear mDNSResponder cache
17785 err = systemf( NULL, "killall -HUP mDNSResponder" );
17786 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
17787
17788 // initialize the global parameters
17789 ExpensiveConstrainedInitializeContext( context );
17790
17791 // The local DNS server is set up on the local only interface.
17792 gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
17793 strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
17794
17795 // The local DNS server is unscoped, so we must set our question to unscoped.
17796 context->ifIndex = kDNSServiceInterfaceIndexAny;
17797
17798 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
17799 // use the local dns server set up previously to run the test locally.
17800 require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
17801 SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
17802 errorDescription = buffer );
17803
17804 // get the quesion name
17805 context->name = gExpensiveConstrainedTest_Name;
17806
17807 // set the initial state for the interface
17808 context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
17809 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
17810 require_noerr_action( err, test_failed, errorDescription = "systemf failed");
17811 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
17812
17813 // get question flag
17814 if ( gExpensiveConstrainedTest_DenyExpensive ) context->flags |= kDNSServiceFlagsDenyExpensive;
17815 if ( gExpensiveConstrainedTest_DenyConstrained ) context->flags |= kDNSServiceFlagsDenyConstrained;
17816 if ( gExpensiveConstrainedTest_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
17817 if ( gExpensiveConstrainedTest_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
17818
17819 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
17820 #if( TARGET_OS_WATCH )
17821 context->flags |= kDNSServiceFlagsPathEvaluationDone;
17822 #endif
17823
17824 // start the query
17825 DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
17826
17827 // set the initial test status
17828 context->subtestReport_startTime = NanoTimeGetCurrent();
17829 context->subtestProgress_startTime = NanoTimeGetCurrent();
17830 context->state = TEST_EXPENSIVE_PREPARE; // start from expensive test
17831 context->isExpensiveNow = context->startFromExpensive ? true : false;
17832 context->isConstrainedNow = false;
17833 context->expectedOperation = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
17834 context->operation = NO_UPDATE;
17835 context->subtestProgress = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
17836 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17837 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
17838 require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17839
17840 // set the queue where the callback will be called when there is an answer for the query
17841 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
17842 require_noerr( err, test_failed );
17843
17844 ExpensiveConstrainedStartTestHandler( context );
17845 }
17846 break;
17847 case TEST_EXPENSIVE_PREPARE:
17848 require_action( context->isConstrainedNow == false, test_failed,
17849 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17850 errorDescription = buffer );
17851 require_action( context->expectedOperation == context->operation, test_failed,
17852 errorDescription = "Operation is not expected" );
17853
17854 context->subtestProgress_startTime = NanoTimeGetCurrent();
17855 context->state = TEST_EXPENSIVE; // begin to test expensive flag
17856 context->counter = 0; // the number of test repetition that has passed
17857 context->isExpensivePrev = context->isExpensiveNow;
17858 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
17859 context->isConstrainedPrev = false; // the interface is currently unconstrained
17860 context->isConstrainedNow = false; // the interface will be unconstrained in the current test
17861 if ( gExpensiveConstrainedTest_DenyExpensive )
17862 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
17863 else
17864 context->expectedOperation = NO_UPDATE;
17865 context->operation = NO_UPDATE; // NO_UPDATE means the call back function has not been called
17866 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17867 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17868
17869 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
17870 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17871
17872 // record the starting timestamp
17873 gettimeofday( &context->updateTime, NULL );
17874
17875 break;
17876 case TEST_EXPENSIVE:
17877 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
17878 require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
17879 SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
17880 errorDescription = buffer );
17881 // constrained flag is always turned off when testing expensive
17882 require_action( context->isConstrainedNow == false, test_failed,
17883 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
17884 errorDescription = buffer );
17885 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
17886
17887 context->counter++; // one test repetition has passed
17888 if ( context->counter == TEST_REPETITION ) // expensive test finished
17889 {
17890 // prepare to test constrained flag
17891 context->state = TEST_CONSTRAINED_PREPARE;
17892
17893 // reset the interface
17894 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
17895 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17896
17897 context->isExpensiveNow = false;
17898 context->isConstrainedNow = false;
17899 gettimeofday( &context->updateTime, NULL );
17900 }
17901 else
17902 {
17903 context->subtestProgress_startTime = NanoTimeGetCurrent();
17904 context->isExpensivePrev = context->isExpensiveNow;
17905 context->isExpensiveNow = !context->isExpensiveNow; // flip the expensive status
17906 if ( gExpensiveConstrainedTest_DenyExpensive )
17907 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
17908 else
17909 context->expectedOperation = NO_UPDATE;
17910 context->operation = NO_UPDATE;
17911 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17912 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17913
17914 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
17915 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17916
17917 gettimeofday( &context->updateTime, NULL );
17918 }
17919 break;
17920 case TEST_CONSTRAINED_PREPARE:
17921 // The interface should be inexpensive and unconstrained when the constrained test starts
17922 require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
17923 errorDescription = buffer );
17924 require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17925 errorDescription = buffer );
17926
17927 context->subtestProgress_startTime = NanoTimeGetCurrent();
17928 context->state = TEST_CONSTRAINED; // constrained interface is now under testing
17929 context->counter = 0;
17930 context->isExpensivePrev = false;
17931 context->isExpensiveNow = false;
17932 context->isConstrainedPrev = false;
17933 context->isConstrainedNow = true; // will set constrained flag on the interface
17934 if ( gExpensiveConstrainedTest_DenyConstrained )
17935 context->expectedOperation = RESULT_RMV;
17936 else
17937 context->expectedOperation = NO_UPDATE;
17938 context->operation = NO_UPDATE;
17939 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17940 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17941
17942 // change interface to the constrained one
17943 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
17944 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17945
17946 gettimeofday( &context->updateTime, NULL );
17947 break;
17948 case TEST_CONSTRAINED:
17949 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
17950 require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
17951 SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
17952 errorDescription = buffer );
17953 require_action( context->isExpensiveNow == false, test_failed,
17954 SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
17955 errorDescription = buffer );
17956 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
17957
17958 context->counter++;
17959 if (context->counter == TEST_REPETITION)
17960 {
17961 // test changing expensive and constrained flags at the same time
17962 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
17963
17964 // reset interface
17965 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
17966 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17967
17968 context->isExpensiveNow = false;
17969 context->isConstrainedNow = false;
17970 gettimeofday( &context->updateTime, NULL );
17971 }
17972 else
17973 {
17974 context->subtestProgress_startTime = NanoTimeGetCurrent();
17975 context->isConstrainedPrev = context->isConstrainedNow;
17976 context->isConstrainedNow = !context->isConstrainedNow; // flip constrained flag
17977 if ( gExpensiveConstrainedTest_DenyConstrained )
17978 context->expectedOperation = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
17979 else
17980 context->expectedOperation = NO_UPDATE;
17981 context->operation = NO_UPDATE;
17982 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17983 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17984
17985 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
17986 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17987
17988 gettimeofday(&context->updateTime, NULL);
17989 }
17990 break;
17991 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
17992 // The interface should be inexpensive and unconstrained when the constrained test starts
17993 require_action( context->isExpensiveNow == false, test_failed,
17994 SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
17995 errorDescription = buffer );
17996 require_action( context->isConstrainedNow == false, test_failed,
17997 SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17998 errorDescription = buffer );
17999
18000 // now flip expensive and constrained at the same time
18001 context->subtestProgress_startTime = NanoTimeGetCurrent();
18002 context->state = TEST_EXPENSIVE_CONSTRAINED;
18003 context->counter = 0;
18004 context->isExpensivePrev = false;
18005 context->isExpensiveNow = true;
18006 context->isConstrainedPrev = false;
18007 context->isConstrainedNow = true;
18008 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
18009 context->expectedOperation = RESULT_RMV;
18010 else
18011 context->expectedOperation = NO_UPDATE;
18012 context->operation = NO_UPDATE;
18013 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
18014 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
18015
18016 err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
18017 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
18018
18019 gettimeofday( &context->updateTime, NULL );
18020 break;
18021 case TEST_EXPENSIVE_CONSTRAINED:
18022 // expensive and constrained flag should always be changed
18023 require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
18024 SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
18025 errorDescription = buffer );
18026 require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
18027 require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
18028
18029 context->counter++;
18030 if ( context->counter == TEST_REPETITION )
18031 {
18032 context->state = TEST_SUCCEEDED;
18033 }
18034 else
18035 {
18036 context->subtestProgress_startTime = NanoTimeGetCurrent();
18037 context->isExpensivePrev = context->isExpensiveNow;
18038 context->isExpensiveNow = !context->isExpensiveNow;
18039 context->isConstrainedPrev = context->isConstrainedNow;
18040 context->isConstrainedNow = !context->isConstrainedNow;
18041 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
18042 context->expectedOperation = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
18043 else
18044 context->expectedOperation = NO_UPDATE;
18045 context->operation = NO_UPDATE;
18046 context->subtestProgress_callBack = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
18047 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
18048
18049 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
18050 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
18051
18052 gettimeofday( &context->updateTime, NULL );
18053 }
18054 break;
18055 case TEST_FAILED:
18056 test_failed:
18057 ExpensiveConstrainedSubtestReport( context, errorDescription );
18058 ExpensiveConstrainedStopAndCleanTheTest( context );
18059 if ( context->numOfRetries > 0 )
18060 {
18061 context->state = TEST_BEGIN;
18062 context->numOfRetries--;
18063 break;
18064 }
18065 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
18066 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
18067 {
18068 ExpensiveConstrainedFinalResultReport( context, false );
18069 exit( 2 );
18070 }
18071 if (context->timer == NULL)
18072 {
18073 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
18074 ExpensiveConstrainedFinalResultReport( context, false );
18075 exit( 1 );
18076 }
18077 context->state = TEST_BEGIN;
18078 break;
18079 case TEST_SUCCEEDED:
18080 ExpensiveConstrainedSubtestReport( context, NULL );
18081 ExpensiveConstrainedStopAndCleanTheTest( context );
18082 ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
18083 if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
18084 {
18085 // all the subtests have been run
18086 Boolean hasFailed = false;
18087 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
18088 hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
18089
18090 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
18091 exit( hasFailed ? 2 : 0 );
18092 }
18093 context->state = TEST_BEGIN;
18094 break;
18095 default:
18096 FPrintF( stdout, "unknown error\n" );
18097 exit( 1 );
18098 }
18099 }
18100
18101 //===========================================================================================================================
18102 // ExpensiveConstrainedCallback
18103 //===========================================================================================================================
18104
18105 static void DNSSD_API
18106 ExpensiveConstrainedCallback(
18107 __unused DNSServiceRef inSDRef,
18108 DNSServiceFlags inFlags,
18109 uint32_t inInterfaceIndex,
18110 DNSServiceErrorType inError,
18111 const char * inHostname,
18112 const struct sockaddr * inSockAddr,
18113 __unused uint32_t inTTL,
18114 void * inContext )
18115 {
18116 ExpensiveConstrainedContext * const context = (ExpensiveConstrainedContext *)inContext;
18117 OSStatus err;
18118 const char * addrStr;
18119 char addrStrBuf[ kSockAddrStringMaxSize ];
18120 char inFlagsDescription[ 128 ];
18121 NanoTime64 now;
18122 char nowTimestamp[ 32 ];
18123
18124 switch ( inError ) {
18125 case kDNSServiceErr_NoError:
18126 case kDNSServiceErr_NoSuchRecord:
18127 break;
18128
18129 case kDNSServiceErr_Timeout:
18130 Exit( kExitReason_Timeout );
18131
18132 default:
18133 err = inError;
18134 goto exit;
18135 }
18136
18137 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
18138 {
18139 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
18140 err = kTypeErr;
18141 goto exit;
18142 }
18143
18144 if( !inError )
18145 {
18146 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
18147 require_noerr( err, exit );
18148 addrStr = addrStrBuf;
18149 }
18150 else
18151 {
18152 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
18153 }
18154
18155 now = NanoTimeGetCurrent();
18156 _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
18157 SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
18158 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress_callBack,
18159 "{"
18160 "%kO=%s"
18161 "%kO=%s"
18162 "%kO=%s"
18163 "%kO=%lli"
18164 "%kO=%s"
18165 "}",
18166 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP, nowTimestamp,
18167 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME, inHostname,
18168 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS, inFlagsDescription,
18169 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE, (int64_t) inInterfaceIndex,
18170 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS, addrStr
18171 );
18172 require_noerr_quiet( err, exit );
18173
18174 if ( inFlags & kDNSServiceFlagsMoreComing )
18175 return;
18176
18177 if ( inFlags & kDNSServiceFlagsAdd )
18178 context->operation = RESULT_ADD;
18179 else
18180 context->operation = RESULT_RMV;
18181
18182 gettimeofday(&context->notificationTime, NULL);
18183 exit:
18184 if( err ) exit( 1 );
18185 }
18186
18187 //===========================================================================================================================
18188 // ExpensiveConstrainedInitializeContext
18189 //===========================================================================================================================
18190
18191 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
18192 {
18193 // clear the flags of the previous subtest
18194 context->flags = 0;
18195 context->protocols = 0;
18196
18197 // get the parameter for the current subtest
18198 const ExpensiveConstrainedTestParams *param = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
18199 gExpensiveConstrainedTest_Name = param->qname;
18200 gExpensiveConstrainedTest_DenyExpensive = param->deny_expensive;
18201 gExpensiveConstrainedTest_DenyConstrained = param->deny_constrained;
18202 gExpensiveConstrainedTest_StartFromExpensive = param->start_from_expensive;
18203 gExpensiveConstrainedTest_ProtocolIPv4 = param->ipv4_query;
18204 gExpensiveConstrainedTest_ProtocolIPv6 = param->ipv6_query;
18205 }
18206
18207 //===========================================================================================================================
18208 // ExpensiveConstrainedStopAndCleanTheTest
18209 //===========================================================================================================================
18210
18211 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
18212 {
18213 // Stop the ongoing query
18214 if ( context->opRef != NULL )
18215 DNSServiceRefDeallocate( context->opRef );
18216
18217 context->opRef = NULL;
18218 context->flags = 0;
18219 context->protocols = 0;
18220 }
18221
18222 //===========================================================================================================================
18223 // ExpensiveConstrainedSubtestProgressReport
18224 //===========================================================================================================================
18225
18226 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
18227 {
18228 OSStatus err;
18229 NanoTime64 now;
18230 char startTime[ 32 ];
18231 char endTime[ 32 ];
18232 char expensive[ 32 ];
18233 char constrained[ 32 ];
18234
18235 now = NanoTimeGetCurrent();
18236 _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
18237 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18238
18239 snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
18240 snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
18241
18242 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
18243 "{"
18244 "%kO=%s"
18245 "%kO=%s"
18246 "%kO=%s"
18247 "%kO=%s"
18248 "%kO=%s"
18249 "%kO=%s"
18250 "%kO=%s"
18251 "%kO=%O"
18252 "}",
18253 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME, startTime,
18254 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME, endTime,
18255 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE, ExpensiveConstrainedStateString(context->state),
18256 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT, ExpensiveConstrainedOperationString(context->expectedOperation),
18257 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT, ExpensiveConstrainedOperationString(context->operation),
18258 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW, expensive,
18259 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW, constrained,
18260 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK, context->subtestProgress_callBack
18261 );
18262 require_noerr( err, exit );
18263 ForgetCF( &context->subtestProgress_callBack );
18264 return;
18265
18266 exit:
18267 ErrQuit( 1, "error: %#m\n", err );
18268 }
18269
18270 //===========================================================================================================================
18271 // ExpensiveConstrainedFinalSubtestReport
18272 //===========================================================================================================================
18273
18274 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
18275 {
18276 OSStatus err;
18277 NanoTime64 now;
18278 char startTime[ 32 ];
18279 char endTime[ 32 ];
18280 char flagDescription[ 1024 ];
18281
18282 now = NanoTimeGetCurrent();
18283 _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
18284 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18285 SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
18286
18287 if (error_description != NULL)
18288 {
18289 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
18290 "{"
18291 "%kO=%s"
18292 "%kO=%s"
18293 "%kO=%s"
18294 "%kO=%s"
18295 "%kO=%s"
18296 "%kO=%lli"
18297 "%kO=%s"
18298 "%kO=%O"
18299 "%kO=%s"
18300 "%kO=%O"
18301 "}",
18302 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
18303 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
18304 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
18305 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
18306 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
18307 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
18308 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
18309 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Fail" ),
18310 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR, error_description,
18311 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
18312 );
18313 }
18314 else
18315 {
18316 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
18317 "{"
18318 "%kO=%s"
18319 "%kO=%s"
18320 "%kO=%s"
18321 "%kO=%s"
18322 "%kO=%s"
18323 "%kO=%lli"
18324 "%kO=%s"
18325 "%kO=%O"
18326 "%kO=%O"
18327 "}",
18328 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME, startTime,
18329 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME, endTime,
18330 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME, context->name,
18331 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS, flagDescription,
18332 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS, ExpensiveConstrainedProtocolString( context->protocols ),
18333 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX, (int64_t) context->ifIndex,
18334 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME, context->ifName,
18335 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT, CFSTR( "Pass" ),
18336 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS, context->subtestProgress
18337 );
18338 }
18339
18340 require_noerr( err, exit );
18341 ForgetCF( &context->subtestProgress );
18342 return;
18343 exit:
18344 ErrQuit( 1, "error: %#m\n", err );
18345 }
18346
18347 //===========================================================================================================================
18348 // ExpensiveConstrainedFinalResultReport
18349 //===========================================================================================================================
18350
18351 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
18352 {
18353 OSStatus err;
18354 CFPropertyListRef plist;
18355 NanoTime64 now;
18356 char startTime[ 32 ];
18357 char endTime[ 32 ];
18358
18359 now = NanoTimeGetCurrent();
18360 _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
18361 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18362
18363 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
18364 "{"
18365 "%kO=%s"
18366 "%kO=%s"
18367 "%kO=%b"
18368 "%kO=%O"
18369 "}",
18370 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME, startTime,
18371 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME, endTime,
18372 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED, allPassed,
18373 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT, context->subtestReport
18374 );
18375 require_noerr( err, exit );
18376 ForgetCF( &context->subtestReport );
18377
18378 err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
18379 CFRelease( plist );
18380 require_noerr( err, exit );
18381
18382 return;
18383 exit:
18384 ErrQuit( 1, "error: %#m\n", err );
18385 }
18386
18387 //===========================================================================================================================
18388 // ExpensiveConstrainedProtocolString
18389 //===========================================================================================================================
18390
18391 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
18392 {
18393 const char *str = NULL;
18394 switch ( protocol ) {
18395 case kDNSServiceProtocol_IPv4:
18396 str = "IPv4";
18397 break;
18398 case kDNSServiceProtocol_IPv6:
18399 str = "IPv6";
18400 break;
18401 case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
18402 str = "IPv4 & IPv6";
18403 break;
18404 default:
18405 break;
18406 }
18407 return str;
18408 }
18409
18410 //===========================================================================================================================
18411 // ExpensiveConstrainedStateString
18412 //===========================================================================================================================
18413
18414 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
18415 {
18416 const char *str = NULL;
18417 switch ( state ) {
18418 case TEST_BEGIN:
18419 str = "TEST_BEGIN";
18420 break;
18421 case TEST_EXPENSIVE_PREPARE:
18422 str = "TEST_EXPENSIVE_PREPARE";
18423 break;
18424 case TEST_EXPENSIVE:
18425 str = "TEST_EXPENSIVE";
18426 break;
18427 case TEST_CONSTRAINED_PREPARE:
18428 str = "TEST_CONSTRAINED_PREPARE";
18429 break;
18430 case TEST_CONSTRAINED:
18431 str = "TEST_CONSTRAINED";
18432 break;
18433 case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
18434 str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
18435 break;
18436 case TEST_EXPENSIVE_CONSTRAINED:
18437 str = "TEST_EXPENSIVE_CONSTRAINED";
18438 break;
18439 case TEST_FAILED:
18440 str = "TEST_FAILED";
18441 break;
18442 case TEST_SUCCEEDED:
18443 str = "TEST_SUCCEEDED";
18444 break;
18445 default:
18446 str = "UNKNOWN";
18447 break;
18448 }
18449
18450 return str;
18451 }
18452
18453 //===========================================================================================================================
18454 // ExpensiveConstrainedOperationString
18455 //===========================================================================================================================
18456
18457 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
18458 {
18459 const char *str = NULL;
18460 switch ( operation ) {
18461 case RESULT_ADD:
18462 str = "RESULT_ADD";
18463 break;
18464 case RESULT_RMV:
18465 str = "RESULT_RMV";
18466 break;
18467 case NO_UPDATE:
18468 str = "NO_UPDATE";
18469 break;
18470 default:
18471 str = "UNKNOWN";
18472 break;
18473 }
18474 return str;
18475 }
18476
18477 //===========================================================================================================================
18478 // expensiveConstrainedEndsWith
18479 //===========================================================================================================================
18480 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
18481 {
18482 if ( !str || !suffix )
18483 return false;
18484 size_t lenstr = strlen( str );
18485 size_t lensuffix = strlen( suffix );
18486 if ( lensuffix > lenstr )
18487 return false;
18488 return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
18489 }
18490
18491 //===========================================================================================================================
18492 // DNSProxyTestCmd
18493 //===========================================================================================================================
18494
18495 // DNS Proxy Test Mode Parameters
18496
18497 typedef enum
18498 {
18499 kDNSProxyTestMode_Normal = 0,
18500 kDNSProxyTestMode_ForceAAAASynthesis,
18501 kDNSProxyTestMode_Count
18502
18503 } DNSProxyTestMode;
18504
18505 check_compile_time( kDNSProxyTestMode_Count > 0 );
18506
18507 // DNS Proxy Test DNS64 Prefix Parameters
18508 // See <https://tools.ietf.org/html/rfc6052#section-2.2>.
18509
18510 static const char * const kDNSProxyTestParams_DNS64Prefixes[] =
18511 {
18512 NULL, // No prefix.
18513 "3ffe:ffff::/32", // 32-bit prefix. Note: Prefix is from deprecated 3ffe::/16 block (see RFC 3701).
18514 "2001:db8:ff00::/40", // 40-bit prefix.
18515 "2001:db8:ffff::/48", // 48-bit prefix.
18516 "2001:db8:ffff:ff00::/56", // 56-bit prefix.
18517 "2001:db8:ffff:ffff::/64", // 64-bit prefix.
18518 "2001:db8:ffff:ff00:ffff:ffff::/96" // 96-bit prefix. Note: bits 64 - 71 MUST be zero.
18519 };
18520
18521 // DNS Proxy Test Transport Parameters
18522
18523 typedef enum
18524 {
18525 kDNSProxyTestTransport_UDPv4 = 0,
18526 kDNSProxyTestTransport_TCPv4,
18527 kDNSProxyTestTransport_UDPv6,
18528 kDNSProxyTestTransport_TCPv6,
18529 kDNSProxyTestTransport_Count
18530
18531 } DNSProxyTestTransport;
18532
18533 check_compile_time( kDNSProxyTestTransport_Count > 0 );
18534
18535 // DNS Proxy Test Query Parameters
18536
18537 typedef enum
18538 {
18539 kDNSProxyTestQuery_A = 0,
18540 kDNSProxyTestQuery_AAAA,
18541 kDNSProxyTestQuery_IPv6OnlyA,
18542 kDNSProxyTestQuery_IPv6OnlyAAAA,
18543 kDNSProxyTestQuery_IPv4OnlyAAAA,
18544 kDNSProxyTestQuery_AliasA,
18545 kDNSProxyTestQuery_AliasAAAA,
18546 kDNSProxyTestQuery_AliasIPv6OnlyA,
18547 kDNSProxyTestQuery_AliasIPv6OnlyAAAA,
18548 kDNSProxyTestQuery_AliasIPv4OnlyAAAA,
18549 kDNSProxyTestQuery_NXDomainA,
18550 kDNSProxyTestQuery_NXDomainAAAA,
18551 kDNSProxyTestQuery_ReverseIPv6,
18552 kDNSProxyTestQuery_ReverseIPv6NXDomain,
18553 kDNSProxyTestQuery_ReverseIPv6DNS64,
18554 kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain,
18555 kDNSProxyTestQuery_Count
18556
18557 } DNSProxyTestQuery;
18558
18559 check_compile_time( kDNSProxyTestQuery_Count > 0 );
18560
18561 #define kDNSProxyTestQueryIterationCount 2
18562
18563 typedef struct DNSProxyTest * DNSProxyTestRef;
18564 struct DNSProxyTest
18565 {
18566 dispatch_queue_t queue; // Serial queue for test events.
18567 dispatch_semaphore_t doneSem; // Semaphore to signal when the test is done.
18568 DNSServiceRef probeGAI; // Probe GAI for DNS server.
18569 char * probeHostname; // Probe hostname.
18570 DNSXConnRef dnsProxy; // DNS proxy connection reference.
18571 dispatch_source_t timer; // Timer to put time limit on queries.
18572 mdns_resolver_t resolver; // Resolver to represent the DNS proxy as a DNS service.
18573 CFMutableDictionaryRef report; // Test's report.
18574 CFMutableArrayRef modeResults; // "Weak" pointer to the 1st-level array of mode results.
18575 CFMutableArrayRef prefixResults; // "Weak" pointer to current 2nd-level array of DNS64 prefix results.
18576 CFMutableArrayRef transportResults; // "Weak" pointer to current 3rd-level array of transport results.
18577 CFMutableArrayRef queryResults; // "Weak" pointer to current 4th-level array of query results.
18578 DNSProxyTestMode modeParam; // Current mode parameter.
18579 unsigned int prefixParamIdx; // Current DNS64 prefix parameter index.
18580 DNSProxyTestTransport transportParam; // Current transport parameter.
18581 DNSProxyTestQuery queryParam; // Current query parameter.
18582 unsigned int queryParamIter; // Current query iteration.
18583 uint32_t loopbackIndex; // Loopback interface's index.
18584 mdns_querier_t querier; // Subtest's querier to send queries to DNS proxy.
18585 NanoTime64 startTime; // Subtest's start time.
18586 char * subtestDesc; // Subtest's description.
18587 char * qnameStr; // Subtest's query QNAME as a C string.
18588 uint8_t * qname; // Subtest's query QNAME in label format.
18589 uint16_t qtype; // Subtest's query QTYPE.
18590 unsigned int aliasCount; // Subtest's expected number of CNAMEs in response answer section.
18591 unsigned int answerCount; // Subtest's expected number of QTYPE records.
18592 int responseCode; // Subtest's expected response code.
18593 uint8_t * canonicalName; // Subtest's expected CNAME rdata for reverse IPv6 queries.
18594 uint8_t * answerName; // Subtest's expected PTR rdata for reverse IPv6 queries.
18595 pid_t serverPID; // PID of spawned DNS server.
18596 int subtestCount; // Number of subtests that have completed or are in progress.
18597 int subtestPassCount; // Number of subtests that have passed so far.
18598 int32_t refCount; // Test object's reference count.
18599 OSStatus error; // Overall test's error.
18600 int dns64PrefixBitLen; // Current DNS64 prefix length (valid if > 0).
18601 uint8_t dns64Prefix[ 16 ]; // Current DNS64 prefix (valid if dns64PrefixBitLen > 0).
18602 char tag[ 6 + 1 ]; // Current subtest's random tag to uniquify QNAMEs.
18603 Boolean synthesizedAAAA; // True if the current subtest expects DNS64 synthesized AAAA records.
18604 Boolean startedSubtests; // True if the test has started running subtests.
18605 };
18606
18607 ulog_define_ex( kDNSSDUtilIdentifier, DNSProxyTest, kLogLevelInfo, kLogFlags_None, "DNSProxyTest", NULL );
18608 #define dpt_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSProxyTest ), (LEVEL), __VA_ARGS__ )
18609
18610 static OSStatus _DNSProxyTestCreate( DNSProxyTestRef *outTest );
18611 static OSStatus _DNSProxyTestRun( DNSProxyTestRef inTest, Boolean *outPassed );
18612 static void _DNSProxyTestRetain( DNSProxyTestRef inTest );
18613 static void _DNSProxyTestRelease( DNSProxyTestRef inTest );
18614
18615 static void DNSProxyTestCmd( void )
18616 {
18617 OSStatus err;
18618 OutputFormatType outputFormat;
18619 DNSProxyTestRef test = NULL;
18620 Boolean passed = false;
18621
18622 err = OutputFormatFromArgString( gDNSProxyTest_OutputFormat, &outputFormat );
18623 require_noerr_quiet( err, exit );
18624
18625 err = _DNSProxyTestCreate( &test );
18626 require_noerr( err, exit );
18627
18628 err = _DNSProxyTestRun( test, &passed );
18629 require_noerr( err, exit );
18630
18631 err = OutputPropertyList( test->report, outputFormat, gDNSProxyTest_OutputFilePath );
18632 require_noerr( err, exit );
18633
18634 exit:
18635 if( test ) _DNSProxyTestRelease( test );
18636 gExitCode = err ? 1 : ( passed ? 0 : 2 );
18637 }
18638
18639 //===========================================================================================================================
18640
18641 static void _DNSProxyTestStart( void *inCtx );
18642 static void _DNSProxyTestStop( DNSProxyTestRef inTest, OSStatus inError );
18643 static OSStatus _DNSProxyTestContinue( DNSProxyTestRef inTest, OSStatus inSubtestError, Boolean *outDone );
18644 static const char * _DNSProxyTestGetCurrentDNS64PrefixParam( DNSProxyTestRef inTest );
18645 static OSStatus _DNSProxyTestPrepareMode( DNSProxyTestRef inTest );
18646 static OSStatus _DNSProxyTestPrepareDNSProxy( DNSProxyTestRef inTest );
18647 static OSStatus _DNSProxyTestPrepareResolver( DNSProxyTestRef inTest );
18648 static OSStatus _DNSProxyTestStartQuery( DNSProxyTestRef inTest, Boolean *outSkipQuery );
18649 static OSStatus
18650 _DNSProxyTestSynthesizeIPv6(
18651 const uint8_t * inIPv6Prefix,
18652 int inIPv6PrefixBitLen,
18653 uint32_t inIPv4Addr,
18654 uint8_t outIPv6Addr[ STATIC_PARAM 16 ] );
18655 static void _DNSProxyTestHandleQuerierResult( DNSProxyTestRef inTest );
18656 static OSStatus
18657 _DNSProxyTestVerifyAddressResponse(
18658 const uint8_t * inMsgPtr,
18659 size_t inMsgLen,
18660 const uint8_t * inQName,
18661 uint16_t inQType,
18662 int inResponseCode,
18663 unsigned int inAliasCount,
18664 unsigned int inAnswerCount,
18665 const uint8_t * inDNS64Prefix,
18666 int inDNS64PrefixBitLen );
18667 static OSStatus
18668 _DNSProxyTestVerifyReverseIPv6Response(
18669 const uint8_t * inMsgPtr,
18670 size_t inMsgLen,
18671 const uint8_t * inQName,
18672 int inResponseCode,
18673 const uint8_t * inCanonicalName,
18674 const uint8_t * inAnswerName );
18675 static void DNSSD_API
18676 _DNSProxyTestProbeGAICallback(
18677 DNSServiceRef inSDRef,
18678 DNSServiceFlags inFlags,
18679 uint32_t inInterfaceIndex,
18680 DNSServiceErrorType inError,
18681 const char * inHostname,
18682 const struct sockaddr * inSockAddr,
18683 uint32_t inTTL,
18684 void * inCtx );
18685 static void _DNSProxyTestProbeTimerHandler( void *inCtx );
18686
18687 static OSStatus _DNSProxyTestCreate( DNSProxyTestRef *outTest )
18688 {
18689 OSStatus err;
18690 DNSProxyTestRef obj;
18691
18692 obj = (DNSProxyTestRef) calloc( 1, sizeof( *obj ) );
18693 require_action( obj, exit, err = kNoMemoryErr );
18694
18695 obj->refCount = 1;
18696 obj->error = kInProgressErr;
18697 obj->serverPID = -1;
18698
18699 obj->queue = dispatch_queue_create( "com.apple.dnssdutil.dns-proxy-test", DISPATCH_QUEUE_SERIAL );
18700 require_action( obj->queue, exit, err = kNoResourcesErr );
18701
18702 obj->doneSem = dispatch_semaphore_create( 0 );
18703 require_action( obj->doneSem, exit, err = kNoResourcesErr );
18704
18705 *outTest = obj;
18706 obj = NULL;
18707 err = kNoErr;
18708
18709 exit:
18710 if( obj ) _DNSProxyTestRelease( obj );
18711 return( err );
18712 }
18713
18714 //===========================================================================================================================
18715
18716 static OSStatus _DNSProxyTestRun( DNSProxyTestRef me, Boolean *outPassed )
18717 {
18718 Boolean passed;
18719
18720 dispatch_async_f( me->queue, me, _DNSProxyTestStart );
18721 dispatch_semaphore_wait( me->doneSem, DISPATCH_TIME_FOREVER );
18722
18723 passed = ( !me->error && ( me->subtestPassCount == me->subtestCount ) ) ? true : false;
18724 CFDictionarySetBoolean( me->report, CFSTR( "pass" ), passed );
18725 dpt_ulog( kLogLevelInfo, "Test result: %s\n", passed ? "pass" : "fail" );
18726
18727 if( outPassed ) *outPassed = passed;
18728 return( me->error );
18729 }
18730
18731 //===========================================================================================================================
18732
18733 static void _DNSProxyTestRetain( DNSProxyTestRef me )
18734 {
18735 atomic_add_32( &me->refCount, 1 );
18736 }
18737
18738 //===========================================================================================================================
18739
18740 static void _DNSProxyTestRelease( DNSProxyTestRef me )
18741 {
18742 if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
18743 {
18744 check( !me->probeGAI );
18745 check( !me->probeHostname );
18746 check( !me->dnsProxy );
18747 check( !me->timer );
18748 check( !me->resolver );
18749 check( !me->modeResults );
18750 check( !me->prefixResults );
18751 check( !me->transportResults );
18752 check( !me->queryResults );
18753 check( !me->querier );
18754 check( !me->subtestDesc );
18755 check( !me->qnameStr );
18756 check( !me->qname );
18757 check( !me->canonicalName );
18758 check( !me->answerName );
18759 check( me->serverPID < 0 );
18760 dispatch_forget( &me->queue );
18761 dispatch_forget( &me->doneSem );
18762 ForgetCF( &me->report );
18763 free( me );
18764 }
18765 }
18766
18767 //===========================================================================================================================
18768
18769 #define kDNSProxyTestProbeQueryTimeoutSecs 5
18770
18771 static void _DNSProxyTestStart( void *inCtx )
18772 {
18773 OSStatus err;
18774 const DNSProxyTestRef me = (DNSProxyTestRef) inCtx;
18775 char * serverCmd = NULL;
18776 NanoTime64 startTime;
18777 char startTimeStr[ 32 ];
18778 char tag[ 6 + 1 ];
18779
18780 startTime = NanoTimeGetCurrent();
18781
18782 dpt_ulog( kLogLevelInfo, "Starting test\n" );
18783
18784 me->error = kInProgressErr;
18785 me->loopbackIndex = if_nametoindex( "lo0" );
18786 err = map_global_value_errno( me->loopbackIndex != 0, me->loopbackIndex );
18787 require_noerr_action_quiet( err, exit, dpt_ulog( kLogLevelError, "Failed to get interface index for lo0: %#m", err ) );
18788
18789 serverCmd = NULL;
18790 ASPrintF( &serverCmd, "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --responseDelay 10",
18791 (int64_t) getpid() );
18792 require_action_quiet( serverCmd, exit, err = kUnknownErr );
18793
18794 err = _SpawnCommand( &me->serverPID, "/dev/null", "/dev/null", "%s", serverCmd );
18795 require_noerr( err, exit );
18796
18797 check( !me->probeHostname );
18798 ASPrintF( &me->probeHostname, "tag-dns-proxy-test-probe-%s.ipv4.d.test.",
18799 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
18800 require_action( me->probeHostname, exit, err = kNoMemoryErr );
18801
18802 err = DNSServiceGetAddrInfo( &me->probeGAI, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
18803 me->probeHostname, _DNSProxyTestProbeGAICallback, me );
18804 require_noerr( err, exit );
18805
18806 err = DNSServiceSetDispatchQueue( me->probeGAI, me->queue );
18807 require_noerr( err, exit );
18808
18809 check( !me->timer );
18810 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDNSProxyTestProbeQueryTimeoutSecs ),
18811 kDNSProxyTestProbeQueryTimeoutSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
18812 me->queue, _DNSProxyTestProbeTimerHandler, me, &me->timer );
18813 require_noerr( err, exit );
18814 dispatch_resume( me->timer );
18815
18816 _NanoTime64ToTimestamp( startTime, startTimeStr, sizeof( startTimeStr ) );
18817 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &me->report,
18818 "{"
18819 "%kO=%s" // startTime
18820 "%kO=%s" // serverCmd
18821 "%kO=%s" // probeHostname
18822 "%kO=[%@]" // results
18823 "}",
18824 CFSTR( "startTime" ), startTimeStr,
18825 CFSTR( "serverCmd" ), serverCmd,
18826 CFSTR( "probeHostname" ), me->probeHostname,
18827 CFSTR( "results" ), &me->modeResults );
18828 require_noerr( err, exit );
18829
18830 exit:
18831 FreeNullSafe( serverCmd );
18832 if( err ) _DNSProxyTestStop( me, err );
18833 }
18834
18835 //===========================================================================================================================
18836
18837 static void _DNSProxyTestSubtestCleanup( DNSProxyTestRef inTest );
18838
18839 #define _DNSXForget( X ) ForgetCustom( X, DNSXRefDeAlloc )
18840
18841 static void _DNSProxyTestStop( DNSProxyTestRef me, OSStatus inError )
18842 {
18843 OSStatus err;
18844 NanoTime64 endTime;
18845 char endTimeStr[ 32 ];
18846
18847 endTime = NanoTimeGetCurrent();
18848 me->error = inError;
18849 dpt_ulog( kLogLevelInfo, "Stopping test with error: %#m\n", me->error );
18850
18851 DNSServiceForget( &me->probeGAI );
18852 ForgetMem( &me->probeHostname );
18853 _DNSXForget( &me->dnsProxy );
18854 dispatch_source_forget( &me->timer );
18855 mdns_resolver_forget( &me->resolver );
18856 me->prefixResults = NULL;
18857 me->modeResults = NULL;
18858 me->transportResults = NULL;
18859 me->queryResults = NULL;
18860 _DNSProxyTestSubtestCleanup( me );
18861 if( me->serverPID >= 0 )
18862 {
18863 OSStatus killErr;
18864
18865 killErr = kill( me->serverPID, SIGTERM );
18866 killErr = map_global_noerr_errno( killErr );
18867 check_noerr( killErr );
18868 me->serverPID = -1;
18869 }
18870 _NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
18871 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
18872 "%kO=%s" // endTime
18873 "%kO=%lli" // subtestCount
18874 "%kO=%lli", // subtestPassCount
18875 CFSTR( "endTime" ), endTimeStr,
18876 CFSTR( "subtestCount" ), (int64_t) me->subtestCount,
18877 CFSTR( "subtestPassCount" ), (int64_t) me->subtestPassCount );
18878 check_noerr( err );
18879 if( err && !me->error ) me->error = err;
18880 dispatch_semaphore_signal( me->doneSem );
18881 }
18882
18883 //===========================================================================================================================
18884
18885 static void _DNSProxyTestSubtestCleanup( DNSProxyTestRef me )
18886 {
18887 dispatch_source_forget( &me->timer );
18888 mdns_querier_forget( &me->querier );
18889 ForgetMem( &me->subtestDesc );
18890 ForgetMem( &me->qnameStr );
18891 ForgetMem( &me->qname );
18892 ForgetMem( &me->canonicalName );
18893 ForgetMem( &me->answerName );
18894 }
18895
18896 //===========================================================================================================================
18897
18898 static OSStatus _DNSProxyTestHandleSubtestCompletion( DNSProxyTestRef inTest, OSStatus inSubtestError );
18899 static char * _DNSProxyTestCreateSubtestDescription( DNSProxyTestRef inTest );
18900 static const char * _DNSProxyTestQueryToString( DNSProxyTestQuery inQuery );
18901 static const char * _DNSProxyTestTransportToString( DNSProxyTestTransport inTransport );
18902 static const char * _DNSProxyTestModeToString( DNSProxyTestMode inMode );
18903
18904 static OSStatus _DNSProxyTestContinue( DNSProxyTestRef me, OSStatus inSubtestError, Boolean *outDone )
18905 {
18906 OSStatus err;
18907 Boolean skipQueries = false;
18908 Boolean done = false;
18909
18910 do
18911 {
18912 if( me->startedSubtests )
18913 {
18914 if( !skipQueries )
18915 {
18916 err = _DNSProxyTestHandleSubtestCompletion( me, inSubtestError );
18917 require_noerr( err, exit );
18918 }
18919 else
18920 {
18921 dpt_ulog( kLogLevelInfo, "Skipped subtest: %s\n", me->subtestDesc );
18922 }
18923 _DNSProxyTestSubtestCleanup( me );
18924 if( skipQueries || ( ++me->queryParamIter == kDNSProxyTestQueryIterationCount ) )
18925 {
18926 me->queryParamIter = 0;
18927 if( ++me->queryParam == kDNSProxyTestQuery_Count )
18928 {
18929 me->queryParam = 0;
18930 me->queryResults = NULL;
18931 dpt_ulog( kLogLevelInfo, "Invalidating resolver: %@\n", me->resolver );
18932 mdns_resolver_forget( &me->resolver );
18933 if( ++me->transportParam == kDNSProxyTestTransport_Count )
18934 {
18935 me->transportParam = 0;
18936 me->transportResults = NULL;
18937 dpt_ulog( kLogLevelInfo, "Disabling DNS proxy\n" );
18938 _DNSXForget( &me->dnsProxy );
18939 if( ++me->prefixParamIdx == countof( kDNSProxyTestParams_DNS64Prefixes ) )
18940 {
18941 me->prefixParamIdx = 0;
18942 me->prefixResults = NULL;
18943 if( ++me->modeParam == kDNSProxyTestMode_Count )
18944 {
18945 me->modeParam = 0;
18946 me->modeResults = NULL;
18947 done = true;
18948 err = kNoErr;
18949 goto exit;
18950 }
18951 }
18952 }
18953 }
18954 }
18955 }
18956 else
18957 {
18958 me->startedSubtests = true;
18959 }
18960 if( ( me->queryParamIter == 0 ) && ( me->queryParam == 0 ) )
18961 {
18962 if( me->transportParam == 0 )
18963 {
18964 if( me->prefixParamIdx == 0 )
18965 {
18966 err = _DNSProxyTestPrepareMode( me );
18967 require_noerr( err, exit );
18968 }
18969 err = _DNSProxyTestPrepareDNSProxy( me );
18970 require_noerr( err, exit );
18971 }
18972 err = _DNSProxyTestPrepareResolver( me );
18973 require_noerr( err, exit );
18974 }
18975 err = _DNSProxyTestStartQuery( me, &skipQueries );
18976 require_noerr( err, exit );
18977
18978 check( !me->subtestDesc );
18979 me->subtestDesc = _DNSProxyTestCreateSubtestDescription( me );
18980 require_action( me->subtestDesc, exit, err = kNoMemoryErr );
18981
18982 } while( skipQueries );
18983
18984 ++me->subtestCount;
18985 dpt_ulog( kLogLevelInfo, "Started subtest #%d: %s\n", me->subtestCount, me->subtestDesc );
18986
18987 exit:
18988 if( outDone ) *outDone = done;
18989 return( err );
18990 }
18991
18992 static OSStatus _DNSProxyTestHandleSubtestCompletion( DNSProxyTestRef me, OSStatus inSubtestError )
18993 {
18994 OSStatus err;
18995 NanoTime64 endTime;
18996 char errorStr[ 128 ];
18997 char startTimeStr[ 32 ];
18998 char endTimeStr[ 32 ];
18999
19000 endTime = NanoTimeGetCurrent();
19001
19002 if( !inSubtestError ) ++me->subtestPassCount;
19003
19004 dpt_ulog( kLogLevelInfo, "Subtest #%d result: %s (pass rate: %d/%d)\n",
19005 me->subtestCount, inSubtestError ? "fail" : "pass", me->subtestPassCount, me->subtestCount );
19006
19007 _NanoTime64ToTimestamp( me->startTime, startTimeStr, sizeof( startTimeStr ) );
19008 _NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
19009 SNPrintF( errorStr, sizeof( errorStr ), "%m", inSubtestError );
19010 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->queryResults,
19011 "{"
19012 "%kO=%s" // description
19013 "%kO=%s" // startTime
19014 "%kO=%s" // endTime
19015 "%kO=%s" // qname
19016 "%kO=%s" // qtype
19017 "%kO=%b" // pass
19018 "%kO=" // error
19019 "{"
19020 "%kO=%lli" // code
19021 "%kO=%s" // description
19022 "}"
19023 "}",
19024 CFSTR( "description" ), me->subtestDesc,
19025 CFSTR( "startTime" ), startTimeStr,
19026 CFSTR( "endTime" ), endTimeStr,
19027 CFSTR( "qname" ), me->qnameStr,
19028 CFSTR( "qtype" ), DNSRecordTypeValueToString( me->qtype ),
19029 CFSTR( "pass" ), inSubtestError ? false : true,
19030 CFSTR( "error" ),
19031 CFSTR( "code" ), (int64_t) inSubtestError,
19032 CFSTR( "description" ), errorStr );
19033 return( err );
19034 }
19035
19036 static char * _DNSProxyTestCreateSubtestDescription( DNSProxyTestRef me )
19037 {
19038 const char * queryStr;
19039 const char * transportStr;
19040 const char * dns64PrefixStr;
19041 const char * modeStr;
19042 char * description;
19043
19044 queryStr = _DNSProxyTestQueryToString( me->queryParam );
19045 transportStr = _DNSProxyTestTransportToString( me->transportParam );
19046 dns64PrefixStr = _DNSProxyTestGetCurrentDNS64PrefixParam( me );
19047 modeStr = _DNSProxyTestModeToString( me->modeParam );
19048 description = NULL;
19049 if( dns64PrefixStr )
19050 {
19051 ASPrintF( &description, "%s over %s to DNS proxy using DNS64 prefix %s in %s mode (%u of %d)",
19052 queryStr, transportStr, dns64PrefixStr, modeStr, me->queryParamIter + 1, kDNSProxyTestQueryIterationCount );
19053 }
19054 else
19055 {
19056 ASPrintF( &description, "%s over %s to DNS proxy in %s mode (%u of %d)",
19057 queryStr, transportStr, modeStr, me->queryParamIter + 1, kDNSProxyTestQueryIterationCount );
19058 }
19059 return( description );
19060 }
19061
19062 //===========================================================================================================================
19063
19064 static const char * _DNSProxyTestQueryToString( DNSProxyTestQuery inQuery )
19065 {
19066 switch( inQuery )
19067 {
19068 case kDNSProxyTestQuery_A: return( "A record query" );
19069 case kDNSProxyTestQuery_AAAA: return( "AAAA record query" );
19070 case kDNSProxyTestQuery_IPv6OnlyA: return( "IPv6-only A record query" );
19071 case kDNSProxyTestQuery_IPv6OnlyAAAA: return( "IPv6-only AAAA record query" );
19072 case kDNSProxyTestQuery_IPv4OnlyAAAA: return( "IPv4-only AAAA record query" );
19073 case kDNSProxyTestQuery_AliasA: return( "A record query with CNAMEs" );
19074 case kDNSProxyTestQuery_AliasAAAA: return( "AAAA record query with CNAMEs" );
19075 case kDNSProxyTestQuery_AliasIPv6OnlyA: return( "IPv6-only A record query with CNAMEs" );
19076 case kDNSProxyTestQuery_AliasIPv6OnlyAAAA: return( "IPv6-only AAAA record query with CNAMEs" );
19077 case kDNSProxyTestQuery_AliasIPv4OnlyAAAA: return( "IPv4-only AAAA record query with CNAMEs" );
19078 case kDNSProxyTestQuery_NXDomainA: return( "A record query (NXDomain)" );
19079 case kDNSProxyTestQuery_NXDomainAAAA: return( "AAAA record query (NXDomain)" );
19080 case kDNSProxyTestQuery_ReverseIPv6: return( "Reverse IPv6 query" );
19081 case kDNSProxyTestQuery_ReverseIPv6NXDomain: return( "Reverse IPv6 query (NXDomain)" );
19082 case kDNSProxyTestQuery_ReverseIPv6DNS64: return( "Reverse IPv6 query with DNS64 prefix" );
19083 case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain: return( "Reverse IPv6 query with DNS64 prefix (NXDomain)" );
19084 default: return( "<INVALID QUERY>" );
19085 }
19086 }
19087
19088 //===========================================================================================================================
19089
19090 static const char * _DNSProxyTestTransportToString( DNSProxyTestTransport inTransport )
19091 {
19092 switch( inTransport )
19093 {
19094 case kDNSProxyTestTransport_UDPv4: return( "IPv4-UDP" );
19095 case kDNSProxyTestTransport_TCPv4: return( "IPv4-TCP" );
19096 case kDNSProxyTestTransport_UDPv6: return( "IPv6-UDP" );
19097 case kDNSProxyTestTransport_TCPv6: return( "IPv6-TCP" );
19098 default: return( "<INVALID TRANSPORT>" );
19099 }
19100 }
19101
19102 //===========================================================================================================================
19103
19104 static const char * _DNSProxyTestModeToString( DNSProxyTestMode inMode )
19105 {
19106 switch( inMode )
19107 {
19108 case kDNSProxyTestMode_Normal: return( "normal" );
19109 case kDNSProxyTestMode_ForceAAAASynthesis: return( "force-AAAA-synthesis" );
19110 default: return( "<INVALID MODE>" );
19111 }
19112 }
19113
19114 //===========================================================================================================================
19115
19116 static const char * _DNSProxyTestGetCurrentDNS64PrefixParam( DNSProxyTestRef me )
19117 {
19118 const unsigned int index = me->prefixParamIdx;
19119 const unsigned int count = countof( kDNSProxyTestParams_DNS64Prefixes );
19120
19121 require_fatal( index < count, "DNSProxyTest DNS Proxy DNS64 prefix parameter index %u out-of-range.", index );
19122 return( kDNSProxyTestParams_DNS64Prefixes[ index ] );
19123 }
19124
19125 //===========================================================================================================================
19126
19127 static OSStatus _DNSProxyTestPrepareMode( DNSProxyTestRef me )
19128 {
19129 OSStatus err;
19130
19131 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->modeResults,
19132 "{"
19133 "%kO=%s" // mode
19134 "%kO=[%@]" // results
19135 "}",
19136 CFSTR( "mode" ), _DNSProxyTestModeToString( me->modeParam ),
19137 CFSTR( "results" ), &me->prefixResults );
19138 return( err );
19139 }
19140
19141 //===========================================================================================================================
19142
19143 static void _DNSProxyTestDNSProxyCallback( DNSXConnRef inDNSProxy, DNSXErrorType inError );
19144
19145 static OSStatus _DNSProxyTestPrepareDNSProxy( DNSProxyTestRef me )
19146 {
19147 OSStatus err;
19148 const char * dns64PrefixStr;
19149 IfIndex interfaces[ MaxInputIf ];
19150
19151 me->dns64PrefixBitLen = 0;
19152 memset( me->dns64Prefix, 0, sizeof( me->dns64Prefix ) );
19153
19154 dns64PrefixStr = _DNSProxyTestGetCurrentDNS64PrefixParam( me );
19155 if( dns64PrefixStr )
19156 {
19157 const char * end;
19158
19159 err = _StringToIPv6Address( dns64PrefixStr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoScope,
19160 me->dns64Prefix, NULL, NULL, &me->dns64PrefixBitLen, &end );
19161 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19162 require_noerr_quiet( err, exit );
19163 }
19164 memset( interfaces, 0, sizeof( interfaces ) );
19165 interfaces[ 0 ] = me->loopbackIndex;
19166
19167 check( !me->dnsProxy );
19168 sleep( 1 ); // Workaround: Allow one second for previous DNS proxy state to get cleaned up.
19169 if( dns64PrefixStr )
19170 {
19171 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
19172 {
19173 DNSXProxyFlags flags;
19174
19175 switch( me->modeParam )
19176 {
19177 case kDNSProxyTestMode_Normal:
19178 flags = kDNSXProxyFlagNull;
19179 break;
19180
19181 case kDNSProxyTestMode_ForceAAAASynthesis:
19182 flags = kDNSXProxyFlagForceAAAASynthesis;
19183 break;
19184
19185 default:
19186 FatalErrorF( "Unhandled DNSProxyTestMode value %ld", (long) me->modeParam );
19187 }
19188 dpt_ulog( kLogLevelInfo, "Enabling DNS proxy with DNS64 prefix %.16a/%d\n",
19189 me->dns64Prefix, me->dns64PrefixBitLen );
19190 err = DNSXEnableProxy64( &me->dnsProxy, kDNSProxyEnable, interfaces, 0, me->dns64Prefix, me->dns64PrefixBitLen,
19191 flags, me->queue, _DNSProxyTestDNSProxyCallback );
19192 require_noerr_quiet( err, exit );
19193 }
19194 else
19195 {
19196 dpt_ulog( kLogLevelError, "DNSXEnableProxy64() is not available on this OS.\n" );
19197 err = kUnsupportedErr;
19198 goto exit;
19199 }
19200 }
19201 else
19202 {
19203 dpt_ulog( kLogLevelInfo, "Enabling DNS proxy (without a DNS64 prefix)\n" );
19204 err = DNSXEnableProxy( &me->dnsProxy, kDNSProxyEnable, interfaces, 0, me->queue, _DNSProxyTestDNSProxyCallback );
19205 require_noerr_quiet( err, exit );
19206 }
19207 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->prefixResults,
19208 "{"
19209 "%kO=%s" // dns64Prefix
19210 "%kO=[%@]" // results
19211 "}",
19212 CFSTR( "dns64Prefix" ), dns64PrefixStr ? dns64PrefixStr : "",
19213 CFSTR( "results" ), &me->transportResults );
19214 require_noerr( err, exit );
19215
19216 exit:
19217 return( err );
19218 }
19219
19220 static void _DNSProxyTestDNSProxyCallback( DNSXConnRef inDNSProxy, DNSXErrorType inError )
19221 {
19222 Unused( inDNSProxy );
19223
19224 if( !inError ) dpt_ulog( kLogLevelInfo, "DNS proxy callback: no error\n" );
19225 else dpt_ulog( kLogLevelError, "DNS proxy callback: error %#m\n", inError );
19226 }
19227
19228 //===========================================================================================================================
19229
19230 #define kDNSProxyTestDNSProxyAddrStr_IPv4 "127.0.0.1:53"
19231 #define kDNSProxyTestDNSProxyAddrStr_IPv6 "[::1]:53"
19232
19233 static OSStatus _DNSProxyTestPrepareResolver( DNSProxyTestRef me )
19234 {
19235 OSStatus err;
19236 const char * addrStr;
19237 mdns_address_t addr = NULL;
19238 mdns_resolver_type_t resolverType;
19239
19240 switch( me->transportParam )
19241 {
19242 case kDNSProxyTestTransport_UDPv4:
19243 addrStr = kDNSProxyTestDNSProxyAddrStr_IPv4;
19244 resolverType = mdns_resolver_type_normal;
19245 break;
19246
19247 case kDNSProxyTestTransport_TCPv4:
19248 addrStr = kDNSProxyTestDNSProxyAddrStr_IPv4;
19249 resolverType = mdns_resolver_type_tcp;
19250 break;
19251
19252 case kDNSProxyTestTransport_UDPv6:
19253 addrStr = kDNSProxyTestDNSProxyAddrStr_IPv6;
19254 resolverType = mdns_resolver_type_normal;
19255 break;
19256
19257 case kDNSProxyTestTransport_TCPv6:
19258 addrStr = kDNSProxyTestDNSProxyAddrStr_IPv6;
19259 resolverType = mdns_resolver_type_tcp;
19260 break;
19261
19262 default:
19263 FatalErrorF( "Unhandled DNSProxyTestTransport value %ld", (long) me->transportParam );
19264 }
19265 check( !me->resolver );
19266 me->resolver = mdns_resolver_create( resolverType, 0, &err );
19267 require_noerr( err, exit );
19268
19269 addr = mdns_address_create_from_ip_address_string( addrStr );
19270 require_action( addr, exit, err = kUnknownErr );
19271
19272 err = mdns_resolver_add_server_address( me->resolver, addr );
19273 mdns_forget( &addr );
19274 require_noerr( err, exit );
19275
19276 dpt_ulog( kLogLevelInfo, "Activating resolver: %@\n", me->resolver );
19277 mdns_resolver_activate( me->resolver );
19278
19279 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( me->tag ) - 1, me->tag );
19280
19281 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->transportResults,
19282 "{"
19283 "%kO=%s" // transport
19284 "%kO=[%@]" // results
19285 "}",
19286 CFSTR( "transport" ), _DNSProxyTestTransportToString( me->transportParam ),
19287 CFSTR( "results" ), &me->queryResults );
19288 require_noerr( err, exit );
19289
19290 exit:
19291 mdns_release_null_safe( addr );
19292 return( err );
19293 }
19294
19295 //===========================================================================================================================
19296
19297 #define kDNSProxyTestQuerierTimeLimitSecs 5
19298 #define kDNSProxyTestRecordTTL ( 5 * kSecondsPerMinute )
19299 #define kDNSProxyTestAddressCount 4
19300 #define kDNSProxyTestAliasCount 4
19301
19302 static void _DNSProxyTestQuerierTimerHandler( void *inCtx );
19303
19304 static OSStatus _DNSProxyTestStartQuery( DNSProxyTestRef me, Boolean *outSkipQuery )
19305 {
19306 OSStatus err;
19307 mdns_querier_t querier;
19308 uint8_t tmpName[ kDomainNameLengthMax ];
19309 Boolean skipQuery = false;
19310
19311 me->startTime = NanoTimeGetCurrent();
19312 me->qtype = 0;
19313 me->aliasCount = 0;
19314 me->answerCount = 0;
19315 me->responseCode = 0;
19316 me->canonicalName = NULL;
19317 me->answerName = NULL;
19318 me->synthesizedAAAA = false;
19319
19320 check( !me->qnameStr );
19321 switch( me->queryParam )
19322 {
19323 case kDNSProxyTestQuery_A:
19324 me->qtype = kDNSRecordType_A;
19325 me->answerCount = kDNSProxyTestAddressCount;
19326 me->responseCode = kDNSRCode_NoError;
19327 ASPrintF( &me->qnameStr, "count-%u.ttl-%u.tag-a-%s.d.test.",
19328 kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19329 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19330 break;
19331
19332 case kDNSProxyTestQuery_AAAA:
19333 me->qtype = kDNSRecordType_AAAA;
19334 me->answerCount = kDNSProxyTestAddressCount;
19335 me->responseCode = kDNSRCode_NoError;
19336 if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19337 {
19338 me->synthesizedAAAA = true;
19339 }
19340 else
19341 {
19342 me->synthesizedAAAA = false;
19343 }
19344 ASPrintF( &me->qnameStr, "count-%u.ttl-%u.tag-aaaa-%s.d.test.",
19345 kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19346 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19347 break;
19348
19349 case kDNSProxyTestQuery_IPv6OnlyA:
19350 me->qtype = kDNSRecordType_A;
19351 me->answerCount = 0;
19352 me->responseCode = kDNSRCode_NoError;
19353 ASPrintF( &me->qnameStr, "ipv6.ttl-%u.tag-ipv6-only-a-%s.d.test.", kDNSProxyTestRecordTTL, me->tag );
19354 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19355 break;
19356
19357 case kDNSProxyTestQuery_IPv6OnlyAAAA:
19358 me->qtype = kDNSRecordType_AAAA;
19359 me->responseCode = kDNSRCode_NoError;
19360 if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19361 {
19362 me->answerCount = 0;
19363 }
19364 else
19365 {
19366 me->answerCount = kDNSProxyTestAddressCount;
19367 }
19368 ASPrintF( &me->qnameStr, "count-%u.ipv6.ttl-%u.tag-ipv6-only-aaaa-%s.d.test.",
19369 kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19370 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19371 break;
19372
19373 case kDNSProxyTestQuery_IPv4OnlyAAAA:
19374 me->qtype = kDNSRecordType_AAAA;
19375 me->responseCode = kDNSRCode_NoError;
19376 if( me->dns64PrefixBitLen > 0 )
19377 {
19378 me->answerCount = kDNSProxyTestAddressCount;
19379 me->synthesizedAAAA = true;
19380 }
19381 else
19382 {
19383 me->answerCount = 0;
19384 me->synthesizedAAAA = false;
19385 }
19386 ASPrintF( &me->qnameStr, "count-%u.ipv4.ttl-%u.tag-ipv4-only-aaaa-%s.d.test.",
19387 kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19388 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19389 break;
19390
19391 case kDNSProxyTestQuery_AliasA:
19392 me->qtype = kDNSRecordType_A;
19393 me->aliasCount = kDNSProxyTestAliasCount;
19394 me->answerCount = kDNSProxyTestAddressCount;
19395 me->responseCode = kDNSRCode_NoError;
19396 ASPrintF( &me->qnameStr, "alias-%u.count-%u.ttl-%u.tag-alias-a-%s.d.test.",
19397 kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19398 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19399 break;
19400
19401 case kDNSProxyTestQuery_AliasAAAA:
19402 me->qtype = kDNSRecordType_AAAA;
19403 me->aliasCount = kDNSProxyTestAliasCount;
19404 me->answerCount = kDNSProxyTestAddressCount;
19405 me->responseCode = kDNSRCode_NoError;
19406 if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19407 {
19408 me->synthesizedAAAA = true;
19409 }
19410 else
19411 {
19412 me->synthesizedAAAA = false;
19413 }
19414 ASPrintF( &me->qnameStr, "alias-%u.count-%u.ttl-%u.tag-alias-aaaa-%s.d.test.",
19415 kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19416 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19417 break;
19418
19419 case kDNSProxyTestQuery_AliasIPv6OnlyA:
19420 me->qtype = kDNSRecordType_A;
19421 me->aliasCount = kDNSProxyTestAliasCount;
19422 me->answerCount = 0;
19423 me->responseCode = kDNSRCode_NoError;
19424 ASPrintF( &me->qnameStr, "alias-%u.ipv6.ttl-%u.tag-alias-ipv6-only-a-%s.d.test.",
19425 kDNSProxyTestAliasCount, kDNSProxyTestRecordTTL, me->tag );
19426 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19427 break;
19428
19429 case kDNSProxyTestQuery_AliasIPv6OnlyAAAA:
19430 me->qtype = kDNSRecordType_AAAA;
19431 me->aliasCount = kDNSProxyTestAliasCount;
19432 me->responseCode = kDNSRCode_NoError;
19433 if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19434 {
19435 me->answerCount = 0;
19436 }
19437 else
19438 {
19439 me->answerCount = kDNSProxyTestAddressCount;
19440 }
19441 ASPrintF( &me->qnameStr, "alias-%u.ipv6.count-%u.ttl-%u.tag-alias-ipv6-only-aaaa-%s.d.test.",
19442 kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19443 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19444 break;
19445
19446 case kDNSProxyTestQuery_AliasIPv4OnlyAAAA:
19447 me->qtype = kDNSRecordType_AAAA;
19448 me->aliasCount = kDNSProxyTestAliasCount;
19449 me->responseCode = kDNSRCode_NoError;
19450 if( me->dns64PrefixBitLen > 0 )
19451 {
19452 me->answerCount = kDNSProxyTestAddressCount;
19453 me->synthesizedAAAA = true;
19454 }
19455 else
19456 {
19457 me->answerCount = 0;
19458 me->synthesizedAAAA = false;
19459 }
19460 ASPrintF( &me->qnameStr, "alias-%u.count-%u.ipv4.ttl-%u.tag-alias-ipv4-only-aaaa-%s.d.test.",
19461 kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19462 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19463 break;
19464
19465 case kDNSProxyTestQuery_NXDomainA:
19466 me->qtype = kDNSRecordType_A;
19467 me->answerCount = 0;
19468 me->responseCode = kDNSRCode_NXDomain;
19469 ASPrintF( &me->qnameStr, "does-not-exist.tag-nx-domain-a-%s.d.test.", me->tag );
19470 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19471 break;
19472
19473 case kDNSProxyTestQuery_NXDomainAAAA:
19474 me->qtype = kDNSRecordType_AAAA;
19475 me->answerCount = 0;
19476 me->responseCode = kDNSRCode_NXDomain;
19477 ASPrintF( &me->qnameStr, "does-not-exist.tag-nx-domain-aaaa-%s.d.test.", me->tag );
19478 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19479 break;
19480
19481 case kDNSProxyTestQuery_ReverseIPv6:
19482 case kDNSProxyTestQuery_ReverseIPv6NXDomain:
19483 {
19484 unsigned int hostID;
19485 uint8_t ipv6Addr[ 16 ];
19486 char reverseIPv6NameStr[ kReverseIPv6DomainNameBufLen ];
19487
19488 // To force mDNSResponder to have to send a query on the first query iteration, use a different reverse IPv6
19489 // PTR query for each mode/DNS64 prefix/transport combination.
19490
19491 check_compile_time_code( kDNSProxyTestTransport_Count <= 4 );
19492 check_compile_time_code( countof( kDNSProxyTestParams_DNS64Prefixes ) <= 8 );
19493 check_compile_time_code( kDNSProxyTestMode_Count <= 4 );
19494 hostID = ( (unsigned int) me->transportParam ) & 0x03; // Set bits 1 - 0 to transport param.
19495 hostID |= ( me->prefixParamIdx & 0x07 ) << 2; // Set bits 4 - 2 to prefix param index.
19496 hostID |= ( ( (unsigned int) me->modeParam ) & 0x03 ) << 5; // Set bits 6 - 5 to mode param.
19497 hostID |= 1U << 7; // Set bit 7 to ensure a non-zero hostID.
19498 check( ( hostID >= 1 ) && ( hostID <= 255 ) );
19499
19500 memcpy( ipv6Addr, kDNSServerBaseAddrV6, 16 );
19501 ipv6Addr[ 15 ] = (uint8_t) hostID;
19502
19503 me->qtype = kDNSRecordType_PTR;
19504 if( me->queryParam == kDNSProxyTestQuery_ReverseIPv6 )
19505 {
19506 char answerNameStr[ 128 ];
19507 char * dst = answerNameStr;
19508 char * const lim = &answerNameStr[ countof( answerNameStr ) ];
19509 int i;
19510
19511 me->responseCode = kDNSRCode_NoError;
19512
19513 // Create the expected RDATA.
19514
19515 SNPrintF_Add( &dst, lim, "ipv6" );
19516 for( i = 0; i < 8; ++i )
19517 {
19518 SNPrintF_Add( &dst, lim, "-%04x", ReadBig16( &ipv6Addr[ i * 2 ] ) );
19519 }
19520 SNPrintF_Add( &dst, lim, ".d.test." );
19521 err = DomainNameFromString( tmpName, answerNameStr, NULL );
19522 require_noerr_quiet( err, exit );
19523
19524 err = DomainNameDup( tmpName, &me->answerName, NULL );
19525 require_noerr( err, exit );
19526 }
19527 else
19528 {
19529 me->responseCode = kDNSRCode_NXDomain;
19530 me->answerName = NULL;
19531
19532 // Make the IPv6 address invalid by setting bits 15-8. This makes the host identifier bogus.
19533 // The DNS server's IPv6 prefix is 96 bits, but only host identifiers in [1, 255] are recognized.
19534
19535 ipv6Addr[ 14 ] = 0xFF;
19536 }
19537 _WriteReverseIPv6DomainNameString( ipv6Addr, reverseIPv6NameStr );
19538 me->qnameStr = strdup( reverseIPv6NameStr );
19539 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19540 break;
19541 }
19542 case kDNSProxyTestQuery_ReverseIPv6DNS64:
19543 case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain:
19544 {
19545 uint32_t ipv4Addr;
19546 uint8_t ipv6Addr[ 16 ];
19547 char reverseIPNameStr[ kReverseIPv6DomainNameBufLen ];
19548
19549 if( me->dns64PrefixBitLen <= 0 )
19550 {
19551 skipQuery = true;
19552 err = kNoErr;
19553 goto exit;
19554 }
19555 me->qtype = kDNSRecordType_PTR;
19556 if( me->queryParam == kDNSProxyTestQuery_ReverseIPv6DNS64 )
19557 {
19558 char answerNameStr[ 64 ];
19559
19560 me->responseCode = kDNSRCode_NoError;
19561
19562 ipv4Addr = kDNSServerBaseAddrV4 + 1;
19563 _WriteReverseIPv4DomainNameString( ipv4Addr, reverseIPNameStr );
19564
19565 err = DomainNameFromString( tmpName, reverseIPNameStr, NULL );
19566 require_noerr_quiet( err, exit );
19567
19568 err = DomainNameDup( tmpName, &me->canonicalName, NULL );
19569 require_noerr( err, exit );
19570
19571 SNPrintF( answerNameStr, sizeof( answerNameStr ), "ipv4-%u-%u-%u-%u.d.test.",
19572 ( ipv4Addr >> 24 ) & 0xFF,
19573 ( ipv4Addr >> 16 ) & 0xFF,
19574 ( ipv4Addr >> 8 ) & 0xFF,
19575 ipv4Addr & 0xFF );
19576 err = DomainNameFromString( tmpName, answerNameStr, NULL );
19577 require_noerr_quiet( err, exit );
19578
19579 err = DomainNameDup( tmpName, &me->answerName, NULL );
19580 require_noerr( err, exit );
19581 }
19582 else
19583 {
19584 ipv4Addr = kDNSServerBaseAddrV4 + 0;
19585
19586 me->responseCode = kDNSRCode_NXDomain;
19587 me->canonicalName = NULL;
19588 me->answerName = NULL;
19589 }
19590 err = _DNSProxyTestSynthesizeIPv6( me->dns64Prefix, me->dns64PrefixBitLen, ipv4Addr, ipv6Addr );
19591 require_noerr( err, exit );
19592
19593 _WriteReverseIPv6DomainNameString( ipv6Addr, reverseIPNameStr );
19594 me->qnameStr = strdup( reverseIPNameStr );
19595 require_action( me->qnameStr, exit, err = kNoMemoryErr );
19596 break;
19597 }
19598 default:
19599 FatalErrorF( "Unhandled DNSProxyTestQuery value %ld", (long) me->queryParam );
19600 }
19601 check( !me->qname );
19602 err = DomainNameFromString( tmpName, me->qnameStr, NULL );
19603 require_noerr_quiet( err, exit );
19604
19605 err = DomainNameDup( tmpName, &me->qname, NULL );
19606 require_noerr( err, exit );
19607
19608 check( !me->querier );
19609 me->querier = mdns_resolver_create_querier( me->resolver, &err );
19610 require_noerr( err, exit );
19611
19612 err = mdns_querier_set_query( me->querier, me->qname, me->qtype, kDNSClassType_IN );
19613 require_noerr( err, exit );
19614
19615 mdns_querier_set_queue( me->querier, me->queue );
19616 _DNSProxyTestRetain( me );
19617 querier = me->querier;
19618 mdns_retain( querier );
19619 mdns_querier_set_result_handler( me->querier,
19620 ^{
19621 if( me->querier == querier ) _DNSProxyTestHandleQuerierResult( me );
19622 _DNSProxyTestRelease( me );
19623 mdns_release( querier );
19624 } );
19625 mdns_querier_activate( me->querier );
19626
19627 check( !me->timer );
19628 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDNSProxyTestQuerierTimeLimitSecs ),
19629 kDNSProxyTestQuerierTimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
19630 me->queue, _DNSProxyTestQuerierTimerHandler, me, &me->timer );
19631 require_noerr( err, exit );
19632 dispatch_resume( me->timer );
19633
19634 exit:
19635 if( outSkipQuery ) *outSkipQuery = skipQuery;
19636 return( err );
19637 }
19638
19639 static void _DNSProxyTestQuerierTimerHandler( void *inCtx )
19640 {
19641 OSStatus err;
19642 const DNSProxyTestRef me = (DNSProxyTestRef) inCtx;
19643 Boolean done;
19644
19645 dpt_ulog( kLogLevelInfo, "Query for '%{du:dname}' timed out.\n", me->qname );
19646
19647 err = _DNSProxyTestContinue( me, kTimeoutErr, &done );
19648 check_noerr( err );
19649 if( err || done ) _DNSProxyTestStop( me, err );
19650 }
19651
19652 //===========================================================================================================================
19653
19654 static OSStatus
19655 _DNSProxyTestSynthesizeIPv6(
19656 const uint8_t * inIPv6Prefix,
19657 int inIPv6PrefixBitLen,
19658 uint32_t inIPv4Addr,
19659 uint8_t outIPv6Addr[ STATIC_PARAM 16 ] )
19660 {
19661 // From <https://tools.ietf.org/html/rfc6052#section-2.2>:
19662 //
19663 // 2.2. IPv4-Embedded IPv6 Address Format
19664 //
19665 // IPv4-converted IPv6 addresses and IPv4-translatable IPv6 addresses
19666 // follow the same format, described here as the IPv4-embedded IPv6
19667 // address Format. IPv4-embedded IPv6 addresses are composed of a
19668 // variable-length prefix, the embedded IPv4 address, and a variable-
19669 // length suffix, as presented in the following diagram, in which PL
19670 // designates the prefix length:
19671 //
19672 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19673 // |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
19674 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19675 // |32| prefix |v4(32) | u | suffix |
19676 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19677 // |40| prefix |v4(24) | u |(8)| suffix |
19678 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19679 // |48| prefix |v4(16) | u | (16) | suffix |
19680 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19681 // |56| prefix |(8)| u | v4(24) | suffix |
19682 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19683 // |64| prefix | u | v4(32) | suffix |
19684 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19685 // |96| prefix | v4(32) |
19686 // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19687 //
19688 // Figure 1
19689 switch( inIPv6PrefixBitLen )
19690 {
19691 case 32:
19692 case 40:
19693 case 48:
19694 case 56:
19695 case 64:
19696 case 96:
19697 {
19698 const int prefixLen = inIPv6PrefixBitLen / 8;
19699 int i, j;
19700 uint8_t v4Addr[ 4 ];
19701
19702 memcpy( outIPv6Addr, inIPv6Prefix, (size_t) prefixLen );
19703 WriteBig32( v4Addr, inIPv4Addr );
19704
19705 // 1. Bits 64 - 71, i.e., reserved octet "u", MUST be zero.
19706 // 2. Except for bits 64 - 71, the 32 bits following the prefix are the bits of the embedded IPv4 address.
19707 // 3. The remaining bits, if any, are the suffix bits, which SHOULD be zero.
19708
19709 j = 0;
19710 for( i = prefixLen; i < 16; ++i )
19711 {
19712 if( ( j < 4 ) && ( i != 8 ) ) outIPv6Addr[ i ] = v4Addr[ j++ ];
19713 else outIPv6Addr[ i ] = 0;
19714 }
19715 return( kNoErr );
19716 }
19717 default:
19718 return( kSizeErr );
19719 }
19720 }
19721
19722 //===========================================================================================================================
19723
19724 static void _DNSProxyTestHandleQuerierResult( DNSProxyTestRef me )
19725 {
19726 OSStatus err, verifyErr;
19727 const uint8_t * msgPtr;
19728 size_t msgLen;
19729 const mdns_querier_result_type_t resultType = mdns_querier_get_result_type( me->querier );
19730 Boolean done = false;
19731
19732 if( resultType == mdns_querier_result_type_response )
19733 {
19734 msgPtr = mdns_querier_get_response_ptr( me->querier );
19735 msgLen = mdns_querier_get_response_length( me->querier );
19736 dpt_ulog( kLogLevelInfo, "Querier response: %.1{du:dnsmsg}\n", msgPtr, msgLen );
19737 }
19738 else
19739 {
19740 if( resultType == mdns_querier_result_type_error )
19741 {
19742 err = mdns_querier_get_error( me->querier );
19743 if( !err ) err = kUnknownErr;
19744 }
19745 else
19746 {
19747 err = kUnexpectedErr;
19748 }
19749 dpt_ulog( kLogLevelError, "Querier result: %s, error: %#m\n", mdns_querier_get_result_type( me->querier ), err );
19750 goto exit;
19751 }
19752 switch( me->queryParam )
19753 {
19754 case kDNSProxyTestQuery_A:
19755 case kDNSProxyTestQuery_AAAA:
19756 case kDNSProxyTestQuery_IPv6OnlyA:
19757 case kDNSProxyTestQuery_IPv6OnlyAAAA:
19758 case kDNSProxyTestQuery_IPv4OnlyAAAA:
19759 case kDNSProxyTestQuery_AliasA:
19760 case kDNSProxyTestQuery_AliasAAAA:
19761 case kDNSProxyTestQuery_AliasIPv6OnlyA:
19762 case kDNSProxyTestQuery_AliasIPv6OnlyAAAA:
19763 case kDNSProxyTestQuery_AliasIPv4OnlyAAAA:
19764 case kDNSProxyTestQuery_NXDomainA:
19765 case kDNSProxyTestQuery_NXDomainAAAA:
19766 verifyErr = _DNSProxyTestVerifyAddressResponse( msgPtr, msgLen, me->qname, me->qtype, me->responseCode,
19767 me->aliasCount, me->answerCount,
19768 me->synthesizedAAAA ? me->dns64Prefix : NULL,
19769 me->synthesizedAAAA ? me->dns64PrefixBitLen : 0 );
19770 break;
19771
19772 case kDNSProxyTestQuery_ReverseIPv6:
19773 case kDNSProxyTestQuery_ReverseIPv6NXDomain:
19774 case kDNSProxyTestQuery_ReverseIPv6DNS64:
19775 case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain:
19776 verifyErr = _DNSProxyTestVerifyReverseIPv6Response( msgPtr, msgLen, me->qname, me->responseCode,
19777 me->canonicalName, me->answerName );
19778 break;
19779
19780 default:
19781 FatalErrorF( "Unhandled DNSProxyTestQuery value %ld", (long) me->queryParam );
19782 }
19783 err = _DNSProxyTestContinue( me, verifyErr, &done );
19784 require_noerr( err, exit );
19785
19786 exit:
19787 if( err || done ) _DNSProxyTestStop( me, err );
19788 }
19789
19790 //===========================================================================================================================
19791
19792 static OSStatus
19793 _DNSProxyTestVerifyAddressResponse(
19794 const uint8_t * inMsgPtr,
19795 size_t inMsgLen,
19796 const uint8_t * inQName,
19797 uint16_t inQType,
19798 int inResponseCode,
19799 unsigned int inAliasCount,
19800 unsigned int inAnswerCount,
19801 const uint8_t * inDNS64Prefix,
19802 int inDNS64PrefixBitLen )
19803 {
19804 OSStatus err;
19805 const DNSHeader * hdr;
19806 const uint8_t * ptr;
19807 const uint8_t * answerSection;
19808 unsigned int flags, qCount, answerCount;
19809 int rcode;
19810 uint16_t qtype, qclass;
19811 uint8_t qname[ kDomainNameLengthMax ];
19812
19813 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kMalformedErr );
19814
19815 hdr = (const DNSHeader *) inMsgPtr;
19816 flags = DNSHeaderGetFlags( hdr );
19817 rcode = DNSFlagsGetRCode( flags );
19818 require_action_quiet( rcode == inResponseCode, exit, err = kValueErr );
19819
19820 qCount = DNSHeaderGetQuestionCount( hdr );
19821 require_action_quiet( qCount == 1, exit, err = kCountErr );
19822
19823 ptr = (const uint8_t *) &hdr[ 1 ];
19824 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
19825 require_noerr_quiet( err, exit );
19826 require_action_quiet( DomainNameEqual( qname, inQName ), exit, err = kNameErr );
19827 require_action_quiet( qtype == inQType, exit, err = kTypeErr );
19828 require_action_quiet( qclass == kDNSClassType_IN, exit, kTypeErr );
19829
19830 answerCount = DNSHeaderGetAnswerCount( hdr );
19831 require_action_quiet( answerCount == ( inAliasCount + inAnswerCount ), exit, err = kCountErr );
19832
19833 answerSection = ptr;
19834 if( inAliasCount > 0 )
19835 {
19836 unsigned int i;
19837 const uint8_t * parentDomain;
19838 uint8_t target[ kDomainNameLengthMax ];
19839
19840 parentDomain = DomainNameGetNextLabel( inQName );
19841 require_fatal( parentDomain, "Invalid qname '%{du:dname}' for non-zero alias count.", inQName );
19842
19843 target[ 0 ] = 0;
19844 err = DomainNameAppendDomainName( target, inQName, NULL );
19845 require_noerr( err, exit );
19846
19847 for( i = 0; i < inAliasCount; ++i )
19848 {
19849 unsigned int j;
19850 const unsigned int aliasNumber = inAliasCount - i;
19851 Boolean foundCNAME = false;
19852
19853 ptr = answerSection;
19854 for( j = 0; j < answerCount; ++j )
19855 {
19856 const uint8_t * rdataPtr;
19857 unsigned int nextAliasNumber;
19858 uint16_t type;
19859 uint16_t class;
19860 uint8_t name[ kDomainNameLengthMax ];
19861 char aliasLabelStr[ 32 ];
19862
19863 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, NULL, &ptr );
19864 require_noerr( err, exit );
19865
19866 if( type != kDNSRecordType_CNAME ) continue;
19867 if( class != kDNSClassType_IN ) continue;
19868 if( !DomainNameEqual( name, target ) ) continue;
19869
19870 target[ 0 ] = 0;
19871 nextAliasNumber = aliasNumber - 1;
19872 if( nextAliasNumber >= 2 )
19873 {
19874 SNPrintF( aliasLabelStr, sizeof( aliasLabelStr ), "alias-%u", nextAliasNumber );
19875 err = DomainNameAppendString( target, aliasLabelStr, NULL );
19876 require_noerr( err, exit );
19877 }
19878 else if( nextAliasNumber == 1 )
19879 {
19880 SNPrintF( aliasLabelStr, sizeof( aliasLabelStr ), "alias" );
19881 err = DomainNameAppendString( target, aliasLabelStr, NULL );
19882 require_noerr( err, exit );
19883 }
19884 err = DomainNameAppendDomainName( target, parentDomain, NULL );
19885 require_noerr( err, exit );
19886
19887 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, name, NULL );
19888 require_noerr( err, exit );
19889
19890 if( !DomainNameEqual( name, target ) ) continue;
19891 foundCNAME = true;
19892 break;
19893 }
19894 require_action_quiet( foundCNAME, exit, err = kNotFoundErr );
19895 }
19896 }
19897 if( inAnswerCount > 0 )
19898 {
19899 const uint8_t * target;
19900 unsigned int i;
19901
19902 if( inAliasCount > 0 )
19903 {
19904 target = DomainNameGetNextLabel( inQName );
19905 require_fatal( target, "Invalid qname '%{du:dname}' for non-zero alias count.", inQName );
19906 }
19907 else
19908 {
19909 target = inQName;
19910 }
19911 for( i = 0; i < inAnswerCount; ++i )
19912 {
19913 unsigned int j;
19914 size_t expectedLen;
19915 uint8_t expectedData[ 16 ];
19916 Boolean foundRecord = false;
19917
19918 if( inQType == kDNSRecordType_A )
19919 {
19920 const uint32_t ipv4Addr = kDNSServerBaseAddrV4 + ( i + 1 );
19921
19922 WriteBig32( expectedData, ipv4Addr );
19923 expectedLen = 4;
19924 }
19925 else
19926 {
19927 if( inDNS64PrefixBitLen != 0 )
19928 {
19929 const uint32_t ipv4Addr = kDNSServerBaseAddrV4 + ( i + 1 );
19930
19931 err = _DNSProxyTestSynthesizeIPv6( inDNS64Prefix, inDNS64PrefixBitLen, ipv4Addr, expectedData );
19932 require_noerr( err, exit );
19933 }
19934 else
19935 {
19936 memcpy( expectedData, kDNSServerBaseAddrV6, 16 );
19937 expectedData[ 15 ] = (uint8_t)( i + 1 );
19938 }
19939 expectedLen = 16;
19940 }
19941 ptr = answerSection;
19942 for( j = 0; j < answerCount; ++j )
19943 {
19944 const uint8_t * rdataPtr;
19945 size_t rdataLen;
19946 uint16_t type;
19947 uint16_t class;
19948 uint8_t name[ kDomainNameLengthMax ];
19949
19950 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, &rdataLen,
19951 &ptr );
19952 require_noerr( err, exit );
19953
19954 if( type != inQType ) continue;
19955 if( class != kDNSClassType_IN ) continue;
19956 if( !DomainNameEqual( name, target ) ) continue;
19957 if( !MemEqual( rdataPtr, rdataLen, expectedData, expectedLen ) ) continue;
19958 foundRecord = true;
19959 break;
19960 }
19961 require_action_quiet( foundRecord, exit, err = kNotFoundErr );
19962 }
19963 }
19964
19965 exit:
19966 return( err );
19967 }
19968
19969 //===========================================================================================================================
19970
19971 static OSStatus
19972 _DNSProxyTestFindDomainNameRecord(
19973 const uint8_t * inMsgPtr,
19974 size_t inMsgLen,
19975 const uint8_t * inRecordSection,
19976 unsigned int inRecordCount,
19977 const uint8_t * inRecordName,
19978 uint16_t inRecordType,
19979 const uint8_t * inRDataName );
19980
19981 static OSStatus
19982 _DNSProxyTestVerifyReverseIPv6Response(
19983 const uint8_t * inMsgPtr,
19984 size_t inMsgLen,
19985 const uint8_t * inQName,
19986 int inResponseCode,
19987 const uint8_t * inCanonicalName,
19988 const uint8_t * inAnswerName )
19989 {
19990 OSStatus err;
19991 const DNSHeader * hdr;
19992 const uint8_t * ptr;
19993 const uint8_t * answerSection;
19994 const uint8_t * ownerName;
19995 unsigned int flags, qCount, answerCount, answerCountExpected;
19996 int rcode;
19997 uint16_t qtype, qclass;
19998 uint8_t qname[ kDomainNameLengthMax ];
19999
20000 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kMalformedErr );
20001
20002 hdr = (const DNSHeader *) inMsgPtr;
20003 flags = DNSHeaderGetFlags( hdr );
20004 rcode = DNSFlagsGetRCode( flags );
20005 require_action_quiet( rcode == inResponseCode, exit, err = kValueErr );
20006
20007 qCount = DNSHeaderGetQuestionCount( hdr );
20008 require_action_quiet( qCount == 1, exit, err = kCountErr );
20009
20010 ptr = (const uint8_t *) &hdr[ 1 ];
20011 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
20012 require_noerr_quiet( err, exit );
20013 require_action_quiet( DomainNameEqual( qname, inQName ), exit, err = kNameErr );
20014 require_action_quiet( qtype == kDNSRecordType_PTR, exit, err = kTypeErr );
20015 require_action_quiet( qclass == kDNSClassType_IN, exit, kTypeErr );
20016
20017 answerCountExpected = ( inCanonicalName ? 1 : 0 ) + ( inAnswerName ? 1 : 0 );
20018 answerCount = DNSHeaderGetAnswerCount( hdr );
20019 require_action_quiet( answerCount == answerCountExpected, exit, err = kCountErr );
20020
20021 answerSection = ptr;
20022 ownerName = inQName;
20023 if( inCanonicalName )
20024 {
20025 err = _DNSProxyTestFindDomainNameRecord( inMsgPtr, inMsgLen, answerSection, answerCount, ownerName,
20026 kDNSRecordType_CNAME, inCanonicalName );
20027 require_noerr( err, exit );
20028
20029 ownerName = inCanonicalName;
20030 }
20031 if( inAnswerName )
20032 {
20033 err = _DNSProxyTestFindDomainNameRecord( inMsgPtr, inMsgLen, answerSection, answerCount, ownerName,
20034 kDNSRecordType_PTR, inAnswerName );
20035 require_noerr( err, exit );
20036 }
20037
20038 exit:
20039 return( err );
20040 }
20041
20042 static OSStatus
20043 _DNSProxyTestFindDomainNameRecord(
20044 const uint8_t * inMsgPtr,
20045 size_t inMsgLen,
20046 const uint8_t * inRecordSection,
20047 unsigned int inRecordCount,
20048 const uint8_t * inRecordName,
20049 uint16_t inRecordType,
20050 const uint8_t * inRDataName )
20051 {
20052 OSStatus err;
20053 const uint8_t * ptr;
20054 unsigned int i;
20055 Boolean found = false;
20056
20057 ptr = inRecordSection;
20058 for( i = 0; i < inRecordCount; ++i )
20059 {
20060 const uint8_t * rdataPtr;
20061 uint16_t type;
20062 uint16_t class;
20063 uint8_t name[ kDomainNameLengthMax ];
20064
20065 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, NULL, &ptr );
20066 require_noerr( err, exit );
20067
20068 if( type != inRecordType ) continue;
20069 if( class != kDNSClassType_IN ) continue;
20070 if( !DomainNameEqual( name, inRecordName ) ) continue;
20071
20072 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, name, NULL );
20073 require_noerr( err, exit );
20074
20075 if( !DomainNameEqual( name, inRDataName ) ) continue;
20076 found = true;
20077 break;
20078 }
20079 err = found ? kNoErr : kNotFoundErr;
20080
20081 exit:
20082 return( err );
20083 }
20084
20085 //===========================================================================================================================
20086
20087 static void DNSSD_API
20088 _DNSProxyTestProbeGAICallback(
20089 DNSServiceRef inSDRef,
20090 DNSServiceFlags inFlags,
20091 uint32_t inInterfaceIndex,
20092 DNSServiceErrorType inError,
20093 const char * inHostname,
20094 const struct sockaddr * inSockAddr,
20095 uint32_t inTTL,
20096 void * inCtx )
20097 {
20098 OSStatus err;
20099 const DNSProxyTestRef me = (DNSProxyTestRef) inCtx;
20100 Boolean done = false;
20101
20102 Unused( inSDRef );
20103 Unused( inInterfaceIndex );
20104 Unused( inHostname );
20105 Unused( inTTL );
20106
20107 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
20108 {
20109 DNSServiceForget( &me->probeGAI );
20110 dispatch_source_forget( &me->timer );
20111
20112 dpt_ulog( kLogLevelInfo, "Probe: Got GAI address %##a for %s\n", inSockAddr, me->probeHostname );
20113
20114 err = _DNSProxyTestContinue( me, kNoErr, &done );
20115 require_noerr( err, exit );
20116 }
20117 err = kNoErr;
20118
20119 exit:
20120 if( err || done ) _DNSProxyTestStop( me, err );
20121 }
20122
20123 //===========================================================================================================================
20124
20125 static void _DNSProxyTestProbeTimerHandler( void *inCtx )
20126 {
20127 const DNSProxyTestRef me = (DNSProxyTestRef) inCtx;
20128
20129 dpt_ulog( kLogLevelInfo, "Probe: GAI request for '%s' timed out.\n", me->probeHostname );
20130 _DNSProxyTestStop( me, kNotPreparedErr );
20131 }
20132
20133 //===========================================================================================================================
20134 // RCodeTestCmd
20135 //===========================================================================================================================
20136
20137 #define kRCodeTestMaxRCodeValue 15
20138
20139 typedef struct RCodeTest * RCodeTestRef;
20140 struct RCodeTest
20141 {
20142 dispatch_queue_t queue; // Serial queue for test events.
20143 dispatch_semaphore_t doneSem; // Semaphore to signal when the test is done.
20144 dnssd_getaddrinfo_t gai; // Current subtest's GAI object. (Also used for probing test DNS server.)
20145 dispatch_source_t timer; // Timer for enforcing time limit on current dnssd_getaddrinfo.
20146 CFMutableDictionaryRef report; // Test's report, as a plist.
20147 CFMutableArrayRef subtestResults; // Pointer to report's subtest results.
20148 CFMutableArrayRef gaiResults; // Array of dnssd_getaddrinfo_result objects for the current GAI.
20149 char * hostname; // Current subtest's hostname. (Also used for probing test DNS server.)
20150 char * description; // Current subtest description.
20151 NanoTime64 startTime; // Current subtest's start time.
20152 pid_t serverPID; // PID of spawned test DNS server.
20153 OSStatus error; // Current test error.
20154 int32_t refCount; // Test's reference count.
20155 int rcode; // Argument to use in current subtest's hostname's RCODE label.
20156 int indexIdx; // Index of argument to use in current subtest's hostname's Index label.
20157 int subtestCount; // Number of subtests that have completed or are in progress.
20158 int subtestPassCount; // Number of subtests that have passed so far.
20159 Boolean hostnameIsAlias; // True if current subtest's hostname is an alias.
20160 Boolean hostnameHasAddr; // True if current subtest's hostname that has an IPv4 address.
20161 Boolean done; // True if all subtests have completed.
20162 };
20163
20164 ulog_define_ex( kDNSSDUtilIdentifier, RCodeTest, kLogLevelInfo, kLogFlags_None, "RCodeTest", NULL );
20165 #define rct_ulog( LEVEL, ... ) ulog( &log_category_from_name( RCodeTest ), (LEVEL), __VA_ARGS__ )
20166
20167 static OSStatus _RCodeTestCreate( RCodeTestRef *outTest );
20168 static OSStatus _RCodeTestRun( RCodeTestRef inTest, Boolean *outPassed );
20169 static void _RCodeTestRetain( RCodeTestRef inTest );
20170 static void _RCodeTestRelease( RCodeTestRef inTest );
20171
20172 static void RCodeTestCmd( void )
20173 {
20174 OSStatus err;
20175 OutputFormatType outputFormat;
20176 RCodeTestRef test = NULL;
20177 Boolean passed = false;
20178
20179 err = CheckRootUser();
20180 require_noerr_quiet( err, exit );
20181
20182 err = OutputFormatFromArgString( gRCodeTest_OutputFormat, &outputFormat );
20183 require_noerr_quiet( err, exit );
20184
20185 err = _RCodeTestCreate( &test );
20186 require_noerr( err, exit );
20187
20188 err = _RCodeTestRun( test, &passed );
20189 require_noerr( err, exit );
20190
20191 err = OutputPropertyList( test->report, outputFormat, gRCodeTest_OutputFilePath );
20192 require_noerr( err, exit );
20193
20194 exit:
20195 if( test ) _RCodeTestRelease( test );
20196 gExitCode = err ? 1 : ( passed ? 0 : 2 );
20197 }
20198
20199 //===========================================================================================================================
20200
20201 static OSStatus _RCodeTestCreate( RCodeTestRef *outTest )
20202 {
20203 OSStatus err;
20204 RCodeTestRef obj;
20205
20206 obj = (RCodeTestRef) calloc( 1, sizeof( *obj ) );
20207 require_action( obj, exit, err = kNoMemoryErr );
20208
20209 obj->refCount = 1;
20210 obj->error = kInProgressErr;
20211 obj->serverPID = -1;
20212
20213 obj->queue = dispatch_queue_create( "com.apple.dnssdutil.rcode-test", DISPATCH_QUEUE_SERIAL );
20214 require_action( obj->queue, exit, err = kNoResourcesErr );
20215
20216 obj->doneSem = dispatch_semaphore_create( 0 );
20217 require_action( obj->doneSem, exit, err = kNoResourcesErr );
20218
20219 obj->report = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
20220 require_action( obj->report, exit, err = kNoMemoryErr );
20221
20222 obj->indexIdx = -1;
20223 *outTest = obj;
20224 obj = NULL;
20225 err = kNoErr;
20226
20227 exit:
20228 if( obj ) _RCodeTestRelease( obj );
20229 return( err );
20230 }
20231
20232 //===========================================================================================================================
20233
20234 static void _RCodeTestStart( void *inCtx );
20235 static void _RCodeTestStop( RCodeTestRef inTest, OSStatus inError );
20236 static OSStatus _RCodeTestStartSubtest( RCodeTestRef inTest );
20237 static OSStatus _RCodeTestContinue( RCodeTestRef inType, OSStatus inSubtestError, Boolean *outDone );
20238 static Boolean _RCodeTestRCodeIsGood( const int inRCode );
20239
20240 static OSStatus _RCodeTestRun( RCodeTestRef me, Boolean *outPassed )
20241 {
20242 Boolean passed;
20243
20244 dispatch_async_f( me->queue, me, _RCodeTestStart );
20245 dispatch_semaphore_wait( me->doneSem, DISPATCH_TIME_FOREVER );
20246
20247 passed = ( !me->error && ( me->subtestPassCount == me->subtestCount ) ) ? true : false;
20248 CFDictionarySetBoolean( me->report, CFSTR( "pass" ), passed );
20249 rct_ulog( kLogLevelInfo, "Test result: %s\n", passed ? "pass" : "fail" );
20250
20251 if( outPassed ) *outPassed = passed;
20252 return( me->error );
20253 }
20254
20255 //===========================================================================================================================
20256
20257 static void _RCodeTestRetain( const RCodeTestRef me )
20258 {
20259 atomic_add_32( &me->refCount, 1 );
20260 }
20261
20262 //===========================================================================================================================
20263
20264 static void _RCodeTestRelease( const RCodeTestRef me )
20265 {
20266 if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
20267 {
20268 check( !me->gai );
20269 check( !me->timer );
20270 check( !me->subtestResults );
20271 check( !me->gaiResults );
20272 check( !me->hostname );
20273 check( !me->description );
20274 check( me->serverPID < 0 );
20275 dispatch_forget( &me->queue );
20276 dispatch_forget( &me->doneSem );
20277 ForgetCF( &me->report );
20278 free( me );
20279 }
20280 }
20281
20282 //===========================================================================================================================
20283
20284 #define kRCodeTestProbeQueryTimeoutSecs 5
20285
20286 static void _RCodeTestHandleGAIProbeResults( RCodeTestRef inTest, dnssd_getaddrinfo_result_t *inResults, size_t inCount );
20287 static void _RCodeTestProbeQueryTimerHandler( void *inCtx );
20288
20289 static void _RCodeTestStart( void * const inCtx )
20290 {
20291 OSStatus err;
20292 const RCodeTestRef me = (RCodeTestRef) inCtx;
20293 char * serverCmd = NULL;
20294 dnssd_getaddrinfo_t gai;
20295 NanoTime64 startTime;
20296 char startTimeStr[ 32 ];
20297 char tag[ 6 + 1 ];
20298
20299 startTime = NanoTimeGetCurrent();
20300 rct_ulog( kLogLevelInfo, "Starting test\n" );
20301
20302 // The "dnssdutil server" command will create a resolver entry for the server's "d.test." domain containing an array
20303 // of the server's IP addresses. Because configd favors IPv6 addresses, when there's a mix of IPv4 and IPv6
20304 // addresses, configd may rearrange the array in order to ensure that IPv6 addresses come before the IPv4 addresses.
20305 // To preserve the original address order, the server is specified to run in IPv6-only mode. This way,
20306 // mDNSResponder's view of the address will be such that address with index value 1 is first, address with index
20307 // value 2 is second, etc.
20308
20309 serverCmd = NULL;
20310 ASPrintF( &serverCmd,
20311 "dnssdutil server --loopback --follow %lld --responseDelay 20 --ipv6 --extraIPv6 3", (int64_t) getpid() );
20312 require_action_quiet( serverCmd, exit, err = kNoMemoryErr );
20313
20314 err = _SpawnCommand( &me->serverPID, "/dev/null", "/dev/null", "%s", serverCmd );
20315 require_noerr( err, exit );
20316
20317 check( !me->hostname );
20318 me->hostname = NULL;
20319 ASPrintF( &me->hostname, "tag-rcode-test-probe-%s.ipv4.d.test.",
20320 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
20321 require_action( me->hostname, exit, err = kNoMemoryErr );
20322
20323 check( !me->gai );
20324 me->gai = dnssd_getaddrinfo_create();
20325 require_action( me->gai, exit, err = kNoResourcesErr );
20326
20327 dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
20328 dnssd_getaddrinfo_set_flags( me->gai, 0 );
20329 dnssd_getaddrinfo_set_interface_index( me->gai, kDNSServiceInterfaceIndexAny );
20330 dnssd_getaddrinfo_set_protocols( me->gai, kDNSServiceProtocol_IPv4 );
20331 dnssd_getaddrinfo_set_queue( me->gai, me->queue );
20332 gai = me->gai;
20333 dnssd_retain( gai );
20334 _RCodeTestRetain( me );
20335 dnssd_getaddrinfo_set_result_handler( me->gai,
20336 ^( dnssd_getaddrinfo_result_t * const inResults, const size_t inCount )
20337 {
20338 require_return( me->gai == gai );
20339 _RCodeTestHandleGAIProbeResults( me, inResults, inCount );
20340 } );
20341 dnssd_getaddrinfo_set_event_handler( me->gai,
20342 ^( const dnssd_event_t inEvent, const DNSServiceErrorType inGAIError )
20343 {
20344 if( inEvent == dnssd_event_invalidated )
20345 {
20346 dnssd_release( gai );
20347 _RCodeTestRelease( me );
20348 }
20349 else if( inEvent == dnssd_event_error )
20350 {
20351 require_return( me->gai == gai );
20352 rct_ulog( kLogLevelError, "dnssd_getaddrinfo error: %#m\n", inGAIError );
20353 _RCodeTestStop( me, inGAIError );
20354 }
20355 } );
20356 dnssd_getaddrinfo_activate( me->gai );
20357
20358 check( !me->timer );
20359 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRCodeTestProbeQueryTimeoutSecs ),
20360 kRCodeTestProbeQueryTimeoutSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
20361 me->queue, _RCodeTestProbeQueryTimerHandler, me, &me->timer );
20362 require_noerr( err, exit );
20363 dispatch_resume( me->timer );
20364
20365 _NanoTime64ToTimestamp( startTime, startTimeStr, sizeof( startTimeStr ) );
20366 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
20367 "%kO=%s" // startTime
20368 "%kO=%s" // serverCmd
20369 "%kO=%s" // probeHostname
20370 "%kO=[%@]", // results
20371 CFSTR( "startTime" ), startTimeStr,
20372 CFSTR( "serverCmd" ), serverCmd,
20373 CFSTR( "probeHostname" ), me->hostname,
20374 CFSTR( "results" ), &me->subtestResults );
20375 require_noerr( err, exit );
20376
20377 exit:
20378 FreeNullSafe( serverCmd );
20379 if( err ) _RCodeTestStop( me, err );
20380 }
20381
20382 static void
20383 _RCodeTestHandleGAIProbeResults(
20384 const RCodeTestRef me,
20385 dnssd_getaddrinfo_result_t * const inResults,
20386 const size_t inCount )
20387 {
20388 size_t i;
20389 Boolean startSubtests = false;
20390
20391 for( i = 0; i < inCount; ++i )
20392 {
20393 const dnssd_getaddrinfo_result_t result = inResults[ i ];
20394
20395 if( dnssd_getaddrinfo_result_get_type( result ) == dnssd_getaddrinfo_result_type_add )
20396 {
20397 rct_ulog( kLogLevelInfo, "Probe GAI got %##a for %s\n",
20398 dnssd_getaddrinfo_result_get_address( result ), me->hostname );
20399 startSubtests = true;
20400 break;
20401 }
20402 }
20403 if( startSubtests )
20404 {
20405 OSStatus err;
20406
20407 dnssd_getaddrinfo_forget( &me->gai );
20408 dispatch_source_forget( &me->timer );
20409 err = _RCodeTestStartSubtest( me );
20410 if( err ) _RCodeTestStop( me, err );
20411 }
20412 }
20413
20414 static void _RCodeTestProbeQueryTimerHandler( void * const inCtx )
20415 {
20416 const RCodeTestRef me = (RCodeTestRef) inCtx;
20417
20418 rct_ulog( kLogLevelInfo, "Probe GAI request for '%s' timed out.\n", me->hostname );
20419 _RCodeTestStop( me, kNotPreparedErr );
20420 }
20421
20422 //===========================================================================================================================
20423
20424 static void _RCodeTestStop( RCodeTestRef me, OSStatus inError )
20425 {
20426 OSStatus err;
20427 NanoTime64 endTime;
20428 char endTimeStr[ 32 ];
20429
20430 endTime = NanoTimeGetCurrent();
20431 me->error = inError;
20432 rct_ulog( kLogLevelInfo, "Stopping test with error: %#m\n", me->error );
20433
20434 dnssd_getaddrinfo_forget( &me->gai );
20435 dispatch_source_forget( &me->timer );
20436 me->subtestResults = NULL;
20437 ForgetCF( &me->gaiResults );
20438 ForgetMem( &me->hostname );
20439 ForgetMem( &me->description );
20440 if( me->serverPID >= 0 )
20441 {
20442 OSStatus killErr;
20443
20444 killErr = kill( me->serverPID, SIGTERM );
20445 killErr = map_global_noerr_errno( killErr );
20446 check_noerr( killErr );
20447 me->serverPID = -1;
20448 }
20449 _NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
20450 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
20451 "%kO=%s" // endTime
20452 "%kO=%lli" // subtestCount
20453 "%kO=%lli", // subtestPassCount
20454 CFSTR( "endTime" ), endTimeStr,
20455 CFSTR( "subtestCount" ), (int64_t) me->subtestCount,
20456 CFSTR( "subtestPassCount" ), (int64_t) me->subtestPassCount );
20457 check_noerr( err );
20458 if( err && !me->error ) me->error = err;
20459 dispatch_semaphore_signal( me->doneSem );
20460 }
20461
20462 //===========================================================================================================================
20463
20464 #define kRCodeSubtestRegularTimeLimitSecs 4
20465 #define kRCodeSubtestExtendedTimeLimitSecs 12 // Allow three seconds for each of the four server addresses.
20466
20467 static void _RCodeSubtestHandleGAIResults( RCodeTestRef inTest, dnssd_getaddrinfo_result_t *inResults, size_t inCount );
20468 static void _RCodeSubtestTimerHandler( void *inCtx );
20469
20470 static const void * _DNSSDObjectCFArrayCallbackRetain( CFAllocatorRef inAllocator, const void *inObject );
20471 static void _DNSSDObjectCFArrayCallbackRelease( CFAllocatorRef inAllocator, const void *inObject );
20472
20473 static const CFArrayCallBacks kDNSSDObjectArrayCallbacks =
20474 {
20475 .version = 0,
20476 .retain = _DNSSDObjectCFArrayCallbackRetain,
20477 .release = _DNSSDObjectCFArrayCallbackRelease
20478 };
20479
20480 // Arguments to use for Index labels. Since the test uses a test DNS server with four IPv6 addresses, which
20481 // mDNSResponder views as four different servers, this is an arbitrary mix of the four possible index values. An Index
20482 // label makes it so that only the server specified by the Index label's argument responds with the correct response.
20483 // The point of the mix is to force mDNSResponder to have to send queries to more than one server before it gets the
20484 // right response.
20485
20486 static const int kRCodeTestIndexArguments[] = { 2, 4, 1, 3, 2, 1, 4, 3 };
20487 #define kRCodeTestMaxIndexArgumentIndex ( countof( kRCodeTestIndexArguments ) - 1 )
20488
20489 static OSStatus _RCodeTestStartSubtest( const RCodeTestRef me )
20490 {
20491 OSStatus err;
20492 dnssd_getaddrinfo_t gai;
20493 const char * rcodeStr;
20494 int index;
20495 unsigned int timeLimitSecs;
20496 char rcodeStrBuf[ 32 ];
20497 char indexLabelStr[ 32 ];
20498 char rcodeLabelStr[ 32 ];
20499 char tag[ 6 + 1 ];
20500
20501 require_action_quiet( !me->done, exit, err = kInternalErr );
20502
20503 check( !me->gai );
20504 check( !me->timer );
20505
20506 me->startTime = NanoTimeGetCurrent();
20507
20508 if( me->indexIdx < 0 )
20509 {
20510 index = -1;
20511 indexLabelStr[ 0 ] = '\0';
20512 require_fatal( ( me->rcode >= 0 ) && ( me->rcode <= kRCodeTestMaxRCodeValue ),
20513 "Unexpected subtest rcode value %d.", me->rcode );
20514 }
20515 else
20516 {
20517 index = kRCodeTestIndexArguments[ me->indexIdx ];
20518 SNPrintF( indexLabelStr, sizeof( indexLabelStr ), "index-%d.", index );
20519 me->hostnameHasAddr = true;
20520 me->hostnameIsAlias = true;
20521 require_fatal( ( me->rcode >= -1 ) && ( me->rcode <= kRCodeTestMaxRCodeValue ),
20522 "Unexpected subtest rcode value %d.", me->rcode );
20523 }
20524 if( me->rcode > 0 )
20525 {
20526 SNPrintF( rcodeLabelStr, sizeof( rcodeLabelStr ), "rcode-%d.", me->rcode );
20527 }
20528 else
20529 {
20530 rcodeLabelStr[ 0 ] = '\0';
20531 }
20532 ForgetMem( &me->hostname );
20533 ASPrintF( &me->hostname, "%s%s%scount-%d.tag-rcode-test-%s.d.test.",
20534 me->hostnameIsAlias ? "alias." : "", indexLabelStr, rcodeLabelStr, me->hostnameHasAddr ? 1 : 0,
20535 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
20536 require_action( me->hostname, exit, err = kNoMemoryErr );
20537
20538 CFReleaseNullSafe( me->gaiResults );
20539 me->gaiResults = CFArrayCreateMutable( NULL, 0, &kDNSSDObjectArrayCallbacks );
20540 require_action( me->gaiResults, exit, err = kNoMemoryErr );
20541
20542 me->gai = dnssd_getaddrinfo_create();
20543 require_action( me->gai, exit, err = kNoResourcesErr );
20544
20545 dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
20546 dnssd_getaddrinfo_set_flags( me->gai, kDNSServiceFlagsReturnIntermediates );
20547 dnssd_getaddrinfo_set_interface_index( me->gai, kDNSServiceInterfaceIndexAny );
20548 dnssd_getaddrinfo_set_protocols( me->gai, kDNSServiceProtocol_IPv4 );
20549 dnssd_getaddrinfo_set_queue( me->gai, me->queue );
20550 gai = me->gai;
20551 dnssd_retain( gai );
20552 _RCodeTestRetain( me );
20553 dnssd_getaddrinfo_set_result_handler( me->gai,
20554 ^( dnssd_getaddrinfo_result_t * const inResults, const size_t inCount )
20555 {
20556 require_return( me->gai == gai );
20557 _RCodeSubtestHandleGAIResults( me, inResults, inCount );
20558 } );
20559 dnssd_getaddrinfo_set_event_handler( gai,
20560 ^( const dnssd_event_t inEvent, const DNSServiceErrorType inGAIError )
20561 {
20562 if( inEvent == dnssd_event_invalidated )
20563 {
20564 dnssd_release( gai );
20565 _RCodeTestRelease( me );
20566 }
20567 else if( inEvent == dnssd_event_error )
20568 {
20569 OSStatus testErr;
20570 Boolean done;
20571
20572 require_return( me->gai == gai );
20573 rct_ulog( kLogLevelError, "dnssd_getaddrinfo error: %#m\n", inGAIError );
20574 testErr = _RCodeTestContinue( me, inGAIError, &done );
20575 if( testErr || done ) _RCodeTestStop( me, testErr );
20576 }
20577 } );
20578
20579 // If an Index label is being used without an RCode label, then only the server specified by the index label will
20580 // respond to the query, so we need more time to allow for query retries due to unresponsive servers.
20581
20582 if( ( index > 0 ) && ( me->rcode < 0 ) )
20583 {
20584 timeLimitSecs = kRCodeSubtestExtendedTimeLimitSecs;
20585 }
20586 else
20587 {
20588 timeLimitSecs = kRCodeSubtestRegularTimeLimitSecs;
20589 }
20590 check( !me->timer );
20591 err = DispatchTimerOneShotCreate( dispatch_time_seconds( timeLimitSecs ),
20592 timeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 20 ),
20593 me->queue, _RCodeSubtestTimerHandler, me, &me->timer );
20594 require_noerr( err, exit );
20595
20596 rcodeStr = DNSRCodeToString( me->rcode );
20597 if( !rcodeStr )
20598 {
20599 SNPrintF( rcodeStrBuf, sizeof( rcodeStrBuf ), "RCODE%d", me->rcode );
20600 rcodeStr = rcodeStrBuf;
20601 }
20602 ForgetMem( &me->description );
20603 if( me->indexIdx < 0 )
20604 {
20605 ASPrintF( &me->description, "DNS response with RCODE %s (%d), %s CNAME record, and %s A record from all servers",
20606 rcodeStr, me->rcode, me->hostnameIsAlias ? "one" : "no", me->hostnameHasAddr ? "one" : "no" );
20607 require_action( me->description, exit, err = kNoMemoryErr );
20608 }
20609 else
20610 {
20611 int n;
20612
20613 ASPrintF( &me->description, "DNS response with RCODE NoError (0), %s CNAME record, and %s A record from server #%d.",
20614 me->hostnameIsAlias ? "one" : "no", me->hostnameHasAddr ? "one" : "no", index );
20615 require_action( me->description, exit, err = kNoMemoryErr );
20616
20617 if( me->rcode < 0 )
20618 {
20619 n = AppendPrintF( &me->description, " No DNS respones from all other servers." );
20620 require_action( n >= 0, exit, err = kNoMemoryErr );
20621 }
20622 else
20623 {
20624 n = AppendPrintF( &me->description, " DNS responses with RCODE %s (%d) and no records from all other servers.",
20625 rcodeStr, me->rcode );
20626 require_action( n >= 0, exit, err = kNoMemoryErr );
20627 }
20628 }
20629 ++me->subtestCount;
20630 rct_ulog( kLogLevelInfo, "Starting subtest #%d: %s\n", me->subtestCount, me->description );
20631
20632 dnssd_getaddrinfo_activate( me->gai );
20633 dispatch_resume( me->timer );
20634
20635 exit:
20636 return( err );
20637 }
20638
20639 static void
20640 _RCodeSubtestHandleGAIResults(
20641 const RCodeTestRef me,
20642 dnssd_getaddrinfo_result_t * const inResults,
20643 const size_t inCount )
20644 {
20645 size_t i;
20646
20647 for( i = 0; i < inCount; ++i )
20648 {
20649 const dnssd_getaddrinfo_result_t result = inResults[ i ];
20650
20651 rct_ulog( kLogLevelInfo, "GAI result -- %@\n", result );
20652 CFArrayAppendValue( me->gaiResults, result );
20653 }
20654 }
20655
20656 static void _RCodeSubtestTimerHandler( void * const inCtx )
20657 {
20658 OSStatus err;
20659 const RCodeTestRef me = (RCodeTestRef) inCtx;
20660 dnssd_getaddrinfo_result_t result;
20661 const sockaddr_ip * sip;
20662 const uint8_t * targetName;
20663 CFIndex n;
20664 dnssd_getaddrinfo_result_type_t resultType;
20665 int expectedResultCount;
20666 uint8_t hostname[ kDomainNameLengthMax ];
20667 uint8_t actualHostname[ kDomainNameLengthMax ];
20668 Boolean expectAddress, expectCanonName, done;
20669
20670 // mDNSResponder has traditionally ignored responses with RCODEs not equal to NoError, NXDomain, or NotAuth.
20671 // Such responses result in a kDNSServiceErr_NoSuchRecord error, or NoAddress in the case of dnssd_getaddrinfo.
20672
20673 if( ( me->indexIdx >= 0 ) || _RCodeTestRCodeIsGood( me->rcode ) )
20674 {
20675 expectAddress = me->hostnameHasAddr;
20676 expectCanonName = me->hostnameIsAlias;
20677 expectedResultCount = 1;
20678 }
20679 else
20680 {
20681 expectAddress = false;
20682 expectCanonName = false;
20683 expectedResultCount = 1;
20684 }
20685 n = CFArrayGetCount( me->gaiResults );
20686 require_action_quiet( n == expectedResultCount, exit, err = kCountErr );
20687
20688 if( n != 0 )
20689 {
20690 result = (dnssd_getaddrinfo_result_t) CFArrayGetValueAtIndex( me->gaiResults, 0 );
20691 resultType = dnssd_getaddrinfo_result_get_type( result );
20692
20693 sip = (const sockaddr_ip *) dnssd_getaddrinfo_result_get_address( result );
20694 require_action_quiet( sip->sa.sa_family == AF_INET, exit, err = kAddressErr );
20695
20696 if( expectAddress )
20697 {
20698 require_action_quiet( resultType == dnssd_getaddrinfo_result_type_add, exit, err = kTypeErr );
20699 require_action_quiet( ntohl( sip->v4.sin_addr.s_addr ) == ( kDNSServerBaseAddrV4 + 1 ), exit, err = kAddressErr );
20700 }
20701 else
20702 {
20703 require_action_quiet( resultType == dnssd_getaddrinfo_result_type_no_address, exit, err = kTypeErr );
20704 }
20705 err = DomainNameFromString( hostname, me->hostname, NULL );
20706 require_noerr_fatal( err, "Failed to convert hostname to DNS wire format -- hostname: %s, error: %#m",
20707 me->hostname, err );
20708
20709 err = DomainNameFromString( actualHostname, dnssd_getaddrinfo_result_get_actual_hostname( result ), NULL );
20710 require_noerr_quiet( err, exit );
20711
20712 if( expectCanonName )
20713 {
20714 size_t firstLabelLen;
20715
20716 firstLabelLen = hostname[ 0 ];
20717 require_fatal( firstLabelLen > 0, "Hostname's first label length is zero -- hostname: %s", me->hostname );
20718
20719 targetName = &hostname[ 1 + firstLabelLen ];
20720 }
20721 else
20722 {
20723 targetName = hostname;
20724 }
20725 require_action_quiet( DomainNameEqual( actualHostname, targetName ), exit, err = kNameErr );
20726 }
20727 err = kNoErr;
20728
20729 exit:
20730 err = _RCodeTestContinue( me, err, &done );
20731 if( err || done ) _RCodeTestStop( me, err );
20732 }
20733
20734 static const void * _DNSSDObjectCFArrayCallbackRetain( __unused const CFAllocatorRef inAllocator, const void *inObject )
20735 {
20736 dnssd_retain( (dnssd_object_t) inObject );
20737 return inObject;
20738 }
20739
20740 static void _DNSSDObjectCFArrayCallbackRelease( __unused const CFAllocatorRef inAllocator, const void *inObject )
20741 {
20742 dnssd_release( (dnssd_object_t) inObject );
20743 }
20744
20745 //===========================================================================================================================
20746
20747 static OSStatus _RCodeTestStopSubtest( RCodeTestRef inTest, OSStatus inError );
20748
20749 static OSStatus _RCodeTestContinue( const RCodeTestRef me, OSStatus inSubtestError, Boolean *outDone )
20750 {
20751 OSStatus err;
20752
20753 require_action_quiet( !me->done, exit, err = kNoErr );
20754
20755 err = _RCodeTestStopSubtest( me, inSubtestError );
20756 require_noerr( err, exit );
20757 require_action_quiet( !me->done, exit, err = kNoErr );
20758
20759 err = _RCodeTestStartSubtest( me );
20760 require_noerr( err, exit );
20761
20762 exit:
20763 if( outDone ) *outDone = me->done;
20764 return( err );
20765 }
20766
20767 //===========================================================================================================================
20768
20769 static OSStatus _RCodeTestStopSubtest( const RCodeTestRef me, const OSStatus inError )
20770 {
20771 OSStatus err;
20772 NanoTime64 endTime;
20773 CFMutableArrayRef gaiResultDescs;
20774 char errorStr[ 128 ];
20775 char startTimeStr[ 32 ];
20776 char endTimeStr[ 32 ];
20777 CFIndex i, n;
20778
20779 dnssd_getaddrinfo_forget( &me->gai );
20780 dispatch_source_forget( &me->timer );
20781
20782 endTime = NanoTimeGetCurrent();
20783
20784 if( !inError ) ++me->subtestPassCount;
20785
20786 rct_ulog( kLogLevelInfo, "Subtest #%d result: %s (pass rate: %d/%d)\n",
20787 me->subtestCount, inError ? "fail" : "pass", me->subtestPassCount, me->subtestCount );
20788
20789 _NanoTime64ToTimestamp( me->startTime, startTimeStr, sizeof( startTimeStr ) );
20790 _NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
20791 SNPrintF( errorStr, sizeof( errorStr ), "%m", inError );
20792 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->subtestResults,
20793 "{"
20794 "%kO=%s" // description
20795 "%kO=%s" // startTime
20796 "%kO=%s" // endTime
20797 "%kO=%s" // hostname
20798 "%kO=%b" // pass
20799 "%kO=" // error
20800 "{"
20801 "%kO=%lli" // code
20802 "%kO=%s" // description
20803 "}"
20804 "%kO=[%@]" // GAIResults
20805 "}",
20806 CFSTR( "description" ), me->description,
20807 CFSTR( "startTime" ), startTimeStr,
20808 CFSTR( "endTime" ), endTimeStr,
20809 CFSTR( "hostname" ), me->hostname,
20810 CFSTR( "pass" ), !inError ? true : false,
20811 CFSTR( "error" ),
20812 CFSTR( "code" ), (int64_t) inError,
20813 CFSTR( "description" ), errorStr,
20814 CFSTR( "GAIResults" ), &gaiResultDescs );
20815 require_noerr( err, exit );
20816
20817 n = CFArrayGetCount( me->gaiResults );
20818 for( i = 0; i < n; ++ i )
20819 {
20820 dnssd_getaddrinfo_result_t result = (dnssd_getaddrinfo_result_t) CFArrayGetValueAtIndex( me->gaiResults, i );
20821 char * resultDesc;
20822
20823 resultDesc = dnssd_copy_description( result );
20824 require_action_quiet( resultDesc, exit, err = kNoMemoryErr );
20825
20826 err = CFPropertyListAppendFormatted( NULL, gaiResultDescs, "%s", resultDesc );
20827 ForgetMem( &resultDesc );
20828 require_noerr( err, exit );
20829 }
20830
20831 if( me->indexIdx < 0 )
20832 {
20833 // Each subtest is one of the 64 possible combinations of
20834 // rcode ∈ {0, 1, 2, …, 15}, hostnameIsAlias ∈ {false, true}, hostnameHasAddr ∈ {false, true}.
20835
20836 if( !me->hostnameHasAddr )
20837 {
20838 me->hostnameHasAddr = true;
20839 }
20840 else
20841 {
20842 me->hostnameHasAddr = false;
20843 if( !me->hostnameIsAlias )
20844 {
20845 me->hostnameIsAlias = true;
20846 }
20847 else
20848 {
20849 me->hostnameIsAlias = false;
20850 if( me->rcode < kRCodeTestMaxRCodeValue )
20851 {
20852 ++me->rcode;
20853 }
20854 else
20855 {
20856 me->indexIdx = 0; // At this point we move on to using Index labels.
20857 me->rcode = -1; // Start with rcode set to -1 to indicate no RCode label.
20858 }
20859 }
20860 }
20861 }
20862 else
20863 {
20864 if( me->indexIdx < (int) kRCodeTestMaxIndexArgumentIndex )
20865 {
20866 ++me->indexIdx;
20867 }
20868 else
20869 {
20870 me->indexIdx = 0;
20871
20872 // When using Index labels to limit responses to one server, we want the other servers to respond
20873 // with bad RCODEs, so if the current RCODE value is good, try incrementing again.
20874
20875 do
20876 {
20877 if( me->rcode < kRCodeTestMaxRCodeValue )
20878 {
20879 ++me->rcode;
20880 }
20881 else
20882 {
20883 me->done = true;
20884 }
20885
20886 } while( !me->done && _RCodeTestRCodeIsGood( me->rcode ) );
20887 }
20888 }
20889
20890 exit:
20891 return( err );
20892 }
20893
20894 //===========================================================================================================================
20895
20896 static Boolean _RCodeTestRCodeIsGood( const int inRCode )
20897 {
20898 return( ( inRCode == kDNSRCode_NoError ) || ( inRCode == kDNSRCode_NXDomain ) || ( inRCode == kDNSRCode_NotAuth ) );
20899 }
20900 #endif // MDNSRESPONDER_PROJECT
20901
20902 //===========================================================================================================================
20903 // RegistrationTestCmd
20904 //===========================================================================================================================
20905
20906 typedef struct RegistrationSubtest RegistrationSubtest;
20907
20908 typedef struct
20909 {
20910 CFMutableArrayRef subtestReports; // Array of subtest reports.
20911 dispatch_source_t timer; // Timer to enforce subtest durations.
20912 dispatch_source_t sigSourceINT; // SIGINT signal handler for a clean test exit.
20913 dispatch_source_t sigSourceTERM; // SIGTERM signal handler for a clean test exit.
20914 RegistrationSubtest * subtest; // Current subtest.
20915 char * outputFilePath; // Path of test result output file. If NULL, stdout will be used.
20916 OutputFormatType outputFormat; // Format of test results output.
20917 CFStringRef computerNamePrev; // Previous ComputerName.
20918 CFStringRef localHostNamePrev; // Previous LocalHostName.
20919 NanoTime64 startTime; // Test's start time.
20920 char * computerName; // Temporary ComputerName to set during testing. (malloc'd)
20921 char * localHostName; // Temporary LocalHostName to set during testing. (malloc'd)
20922 CFStringEncoding computerNamePrevEncoding; // Previous ComputerName's encoding.
20923 int subtestIndex; // Index of current subtest.
20924 Boolean computerNameSet; // True if a temporary ComputerName was set.
20925 Boolean localHostNameSet; // True if a temporary LocalHostName was set.
20926 Boolean failed; // True if at least one non-skipped subtest failed.
20927 Boolean forBATS; // True if the test is running in a BATS environment.
20928
20929 } RegistrationTest;
20930
20931 typedef enum
20932 {
20933 kRegistrationInterfaceSet_Null = 0,
20934 kRegistrationInterfaceSet_All = 1,
20935 kRegistrationInterfaceSet_AllPlusAWDL = 2,
20936 kRegistrationInterfaceSet_LoopbackOnly = 3,
20937 kRegistrationInterfaceSet_AWDLOnly = 4
20938
20939 } RegistrationInterfaceSet;
20940
20941 typedef struct
20942 {
20943 RegistrationInterfaceSet interfaceSet; // Interfaces to register the service over.
20944 Boolean useDefaultName; // True if registration is to use the default service name.
20945 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
20946
20947 } RegistrationSubtestParams;
20948
20949 static const RegistrationSubtestParams kRegistrationSubtestParams[] =
20950 {
20951 { kRegistrationInterfaceSet_All, true, false },
20952 { kRegistrationInterfaceSet_All, false, false },
20953 { kRegistrationInterfaceSet_AllPlusAWDL, true, false },
20954 { kRegistrationInterfaceSet_AllPlusAWDL, false, false },
20955 { kRegistrationInterfaceSet_LoopbackOnly, true, false },
20956 { kRegistrationInterfaceSet_LoopbackOnly, false, false },
20957 { kRegistrationInterfaceSet_AWDLOnly, true, false },
20958 { kRegistrationInterfaceSet_AWDLOnly, false, false },
20959 { kRegistrationInterfaceSet_All, true, true },
20960 { kRegistrationInterfaceSet_All, false, true },
20961 { kRegistrationInterfaceSet_AllPlusAWDL, true, true },
20962 { kRegistrationInterfaceSet_AllPlusAWDL, false, true },
20963 { kRegistrationInterfaceSet_LoopbackOnly, true, true },
20964 { kRegistrationInterfaceSet_LoopbackOnly, false, true },
20965 { kRegistrationInterfaceSet_AWDLOnly, true, true },
20966 { kRegistrationInterfaceSet_AWDLOnly, false, true }
20967 };
20968
20969 typedef struct
20970 {
20971 NanoTime64 browseResultTime; // Per-interface browse result time.
20972 NanoTime64 querySRVResultTime; // Per-interface SRV record query result time.
20973 NanoTime64 queryTXTResultTime; // Per-interface TXT record query result time.
20974
20975 } RegistrationResultTimes;
20976
20977 typedef struct
20978 {
20979 MDNSInterfaceItem base; // Underlying MDNSInterface linked-list item.
20980 RegistrationResultTimes times; // Per-interface result times.
20981
20982 } RegistrationInterfaceItem;
20983
20984 struct RegistrationSubtest
20985 {
20986 DNSServiceRef registration; // DNS-SD service registration.
20987 DNSServiceRef connection; // Shared DNS-SD connection.
20988 DNSServiceRef browse; // DNS-SD browse for service's type.
20989 DNSServiceRef querySRV; // DNS-SD query request for service's SRV record.
20990 DNSServiceRef queryTXT; // DNS-SD query request for service's TXT record.
20991 CFMutableArrayRef unexpected; // Array of unexpected registration, browse, and query results.
20992 #if( TARGET_OS_WATCH )
20993 CFMutableArrayRef ignored; // Array of unexpected, but ignored, browse and query results.
20994 #endif
20995 const char * serviceName; // Service's name.
20996 char * serviceNameCustom; // Service's name if using a custom name. (malloc'd)
20997 char * serviceType; // Service's service type. (malloc'd)
20998 size_t serviceTypeLen; // C string length of service's service type.
20999 char * serviceFQDN; // Service's FQDN, i.e., name of its SRV and TXT records.
21000 uint8_t * txtPtr; // Pointer to service's TXT record data. (malloc'd)
21001 size_t txtLen; // Length of service's TXT record data.
21002 RegistrationInterfaceItem * ifList; // If ifIndex == 0, interfaces that service should register over.
21003 RegistrationResultTimes ifTimes; // If ifIndex != 0, result times for interface with that index.
21004 RegistrationTest * test; // Pointer to parent test.
21005 NanoTime64 startTime; // Subtest's start time.
21006 char * description; // Subtest's description. (malloc'd)
21007 uint32_t ifIndex; // Interface index used for service registration.
21008 uint16_t port; // Service's port number.
21009 Boolean useLODiscovery; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
21010 Boolean includeAWDL; // True if the IncludeAWDL flag was used during registration.
21011 Boolean ifIsAWDL; // True if ifIndex is the index of an AWDL interface.
21012 Boolean skipped; // True if this subtest is to be skipped.
21013 Boolean registered; // True if the test service was successfully registered.
21014 Boolean useDefaultName; // True if the service is to use the default service name.
21015 };
21016
21017 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest );
21018 static void _RegistrationTestFree( RegistrationTest *inTest );
21019 static void _RegistrationTestBegin( void *inContext );
21020 static void _RegistrationTestProceed( RegistrationTest *inTest );
21021 static OSStatus _RegistrationTestStart( RegistrationTest *inTest );
21022 static void _RegistrationTestStop( RegistrationTest *inTest );
21023 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
21024 static OSStatus
21025 _RegistrationTestStartSubtest(
21026 RegistrationTest * inTest,
21027 const RegistrationSubtestParams * inParams,
21028 Boolean * outSkipped );
21029 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest );
21030 static void _RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
21031 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
21032 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
21033 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest );
21034 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest );
21035 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
21036 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
21037 static OSStatus
21038 _RegistrationTestCreateRandomTXTRecord(
21039 size_t inMinLen,
21040 size_t inMaxLen,
21041 uint8_t ** outTXTPtr,
21042 size_t * outTXTLen );
21043 static void DNSSD_API
21044 _RegistrationSubtestRegisterCallback(
21045 DNSServiceRef inSDRef,
21046 DNSServiceFlags inFlags,
21047 DNSServiceErrorType inError,
21048 const char * inName,
21049 const char * inType,
21050 const char * inDomain,
21051 void * inContext );
21052 static void DNSSD_API
21053 _RegistrationSubtestBrowseCallback(
21054 DNSServiceRef inSDRef,
21055 DNSServiceFlags inFlags,
21056 uint32_t inIfIndex,
21057 DNSServiceErrorType inError,
21058 const char * inServiceName,
21059 const char * inServiceType,
21060 const char * inDomain,
21061 void * inContext );
21062 static void DNSSD_API
21063 _RegistrationSubtestQueryCallback(
21064 DNSServiceRef inSDRef,
21065 DNSServiceFlags inFlags,
21066 uint32_t inIfIndex,
21067 DNSServiceErrorType inError,
21068 const char * inName,
21069 uint16_t inType,
21070 uint16_t inClass,
21071 uint16_t inRDataLen,
21072 const void * inRDataPtr,
21073 uint32_t inTTL,
21074 void * inContext );
21075 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
21076 static RegistrationResultTimes *
21077 _RegistrationSubtestGetInterfaceResultTimes(
21078 RegistrationSubtest * inSubtest,
21079 uint32_t inIfIndex,
21080 Boolean * outIsAWDL );
21081 static void _RegistrationTestTimerHandler( void *inContext );
21082 #if( TARGET_OS_WATCH )
21083 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName );
21084 #endif
21085
21086 static void RegistrationTestCmd( void )
21087 {
21088 OSStatus err;
21089 RegistrationTest * test;
21090
21091 err = _RegistrationTestCreate( &test );
21092 require_noerr( err, exit );
21093
21094 if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
21095 if( gRegistrationTest_OutputFilePath )
21096 {
21097 test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
21098 require_action( test->outputFilePath, exit, err = kNoMemoryErr );
21099 }
21100
21101 err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
21102 require_noerr_quiet( err, exit );
21103
21104 dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
21105 dispatch_main();
21106
21107 exit:
21108 if( test ) _RegistrationTestFree( test );
21109 ErrQuit( 1, "error: %#m\n", err );
21110 }
21111
21112 //===========================================================================================================================
21113
21114 static OSStatus _RegistrationTestCreate( RegistrationTest **outTest )
21115 {
21116 OSStatus err;
21117 RegistrationTest * obj;
21118
21119 obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
21120 require_action( obj, exit, err = kNoMemoryErr );
21121
21122 obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21123 require_action( obj->subtestReports, exit, err = kNoMemoryErr );
21124
21125 *outTest = obj;
21126 obj = NULL;
21127 err = kNoErr;
21128
21129 exit:
21130 if( obj ) _RegistrationTestFree( obj );
21131 return( err );
21132 }
21133
21134 //===========================================================================================================================
21135
21136 static void _RegistrationTestFree( RegistrationTest *inTest )
21137 {
21138 check( !inTest->timer );
21139 check( !inTest->sigSourceINT );
21140 check( !inTest->sigSourceTERM );
21141 check( !inTest->computerNameSet );
21142 check( !inTest->localHostNameSet );
21143 check( !inTest->subtest );
21144 ForgetCF( &inTest->subtestReports );
21145 ForgetMem( &inTest->outputFilePath );
21146 ForgetCF( &inTest->computerNamePrev );
21147 ForgetCF( &inTest->localHostNamePrev );
21148 ForgetMem( &inTest->computerName );
21149 ForgetMem( &inTest->localHostName );
21150 }
21151
21152 //===========================================================================================================================
21153
21154 static void _RegistrationTestBegin( void *inContext )
21155 {
21156 _RegistrationTestProceed( (RegistrationTest *) inContext );
21157 }
21158
21159 //===========================================================================================================================
21160
21161 static void _RegistrationTestProceed( RegistrationTest *inTest )
21162 {
21163 OSStatus err;
21164 Boolean skippedSubtest;
21165
21166 do
21167 {
21168 int subtestIndex;
21169
21170 if( !inTest->startTime )
21171 {
21172 err = _RegistrationTestStart( inTest );
21173 require_noerr_quiet( err, exit );
21174
21175 inTest->startTime = NanoTimeGetCurrent();
21176 }
21177 else
21178 {
21179 err = _RegistrationTestEndSubtest( inTest );
21180 require_noerr( err, exit );
21181
21182 ++inTest->subtestIndex;
21183 }
21184
21185 subtestIndex = inTest->subtestIndex;
21186 if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
21187 {
21188 err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
21189 require_noerr_quiet( err, exit );
21190 }
21191 else
21192 {
21193 _RegistrationTestEnd( inTest );
21194 }
21195
21196 } while( skippedSubtest );
21197
21198 exit:
21199 if( err ) _RegistrationTestExit( inTest, err );
21200 }
21201
21202 //===========================================================================================================================
21203
21204 static void _RegistrationTestSignalHandler( void *inContext );
21205
21206 static OSStatus _RegistrationTestStart( RegistrationTest *inTest )
21207 {
21208 OSStatus err;
21209 char tag[ 6 + 1 ];
21210
21211 // Save original ComputerName and LocalHostName.
21212
21213 check( !inTest->computerNamePrev );
21214 inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
21215 err = map_scerror( inTest->computerNamePrev );
21216 require_noerr( err, exit );
21217
21218 check( !inTest->localHostNamePrev );
21219 inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
21220 err = map_scerror( inTest->localHostNamePrev );
21221 require_noerr( err, exit );
21222
21223 // Generate a unique test ComputerName.
21224
21225 check( !inTest->computerName );
21226 ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
21227 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21228 require_action( inTest->computerName, exit, err = kNoMemoryErr );
21229
21230 // Generate a unique test LocalHostName.
21231
21232 check( !inTest->localHostName );
21233 ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
21234 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21235 require_action( inTest->localHostName, exit, err = kNoMemoryErr );
21236
21237 // Set up SIGINT signal handler.
21238
21239 signal( SIGINT, SIG_IGN );
21240 check( !inTest->sigSourceINT );
21241 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
21242 &inTest->sigSourceINT );
21243 require_noerr( err, exit );
21244 dispatch_resume( inTest->sigSourceINT );
21245
21246 // Set up SIGTERM signal handler.
21247
21248 signal( SIGTERM, SIG_IGN );
21249 check( !inTest->sigSourceTERM );
21250 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
21251 &inTest->sigSourceTERM );
21252 require_noerr( err, exit );
21253 dispatch_resume( inTest->sigSourceTERM );
21254
21255 // Set test ComputerName.
21256
21257 check( !inTest->computerNameSet );
21258 err = _SetComputerNameWithUTF8CString( inTest->computerName );
21259 require_noerr( err, exit );
21260 inTest->computerNameSet = true;
21261
21262 // Set test LocalHostName.
21263
21264 check( !inTest->localHostNameSet );
21265 err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
21266 require_noerr( err, exit );
21267 inTest->localHostNameSet = true;
21268
21269 exit:
21270 if( err ) _RegistrationTestStop( inTest );
21271 return( err );
21272 }
21273
21274 static void _RegistrationTestSignalHandler( void *inContext )
21275 {
21276 RegistrationTest * const test = (RegistrationTest *) inContext;
21277
21278 FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
21279
21280 _RegistrationTestExit( test, kCanceledErr );
21281 }
21282
21283 //===========================================================================================================================
21284
21285 static void _RegistrationTestStop( RegistrationTest *inTest )
21286 {
21287 OSStatus err;
21288
21289 dispatch_source_forget( &inTest->timer );
21290 dispatch_source_forget( &inTest->sigSourceINT );
21291 dispatch_source_forget( &inTest->sigSourceTERM );
21292 _RegistrationSubtestForget( &inTest->subtest );
21293 if( inTest->computerNameSet )
21294 {
21295 err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
21296 check_noerr( err );
21297 if( !err ) inTest->computerNameSet = false;
21298 }
21299 if( inTest->localHostNameSet )
21300 {
21301 err = _SetLocalHostName( inTest->localHostNamePrev );
21302 check_noerr( err );
21303 if( !err ) inTest->localHostNameSet = false;
21304 }
21305 }
21306
21307 //===========================================================================================================================
21308
21309 #define kRegistrationTestSubtestDurationSecs 5
21310
21311 static OSStatus
21312 _RegistrationTestStartSubtest(
21313 RegistrationTest * inTest,
21314 const RegistrationSubtestParams * inParams,
21315 Boolean * outSkipped )
21316 {
21317 OSStatus err;
21318 RegistrationSubtest * subtest;
21319 const char * interfaceDesc;
21320 DNSServiceFlags flags;
21321 char tag[ 6 + 1 ];
21322
21323 subtest = NULL;
21324 err = _RegistrationSubtestCreate( &subtest );
21325 require_noerr( err, exit );
21326
21327 subtest->test = inTest;
21328 subtest->useDefaultName = inParams->useDefaultName;
21329 subtest->useLODiscovery = inParams->useLODiscovery;
21330
21331 // Determine registration interfaces.
21332
21333 switch( inParams->interfaceSet )
21334 {
21335 case kRegistrationInterfaceSet_All:
21336 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
21337
21338 if( !subtest->useLODiscovery )
21339 {
21340 err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
21341 require_noerr( err, exit );
21342 }
21343 interfaceDesc = "all interfaces (excluding AWDL)";
21344 break;
21345
21346 case kRegistrationInterfaceSet_AllPlusAWDL:
21347 subtest->ifIndex = kDNSServiceInterfaceIndexAny;
21348 subtest->includeAWDL = true;
21349
21350 if( !subtest->useLODiscovery )
21351 {
21352 err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
21353 require_noerr( err, exit );
21354 }
21355 interfaceDesc = "all interfaces (including AWDL)";
21356 break;
21357
21358 case kRegistrationInterfaceSet_LoopbackOnly:
21359 subtest->ifIndex = if_nametoindex( "lo0" );
21360 if( subtest->ifIndex == 0 )
21361 {
21362 FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
21363 err = kNoResourcesErr;
21364 goto exit;
21365 }
21366 interfaceDesc = "loopback interface";
21367 break;
21368
21369 case kRegistrationInterfaceSet_AWDLOnly:
21370 err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
21371 if( err == kNotFoundErr )
21372 {
21373 FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
21374 subtest->skipped = true;
21375 err = kNoErr;
21376 }
21377 require_noerr( err, exit );
21378
21379 subtest->ifIsAWDL = true;
21380 interfaceDesc = "AWDL interface";
21381 break;
21382
21383 default:
21384 err = kParamErr;
21385 goto exit;
21386 }
21387
21388 // Create description.
21389
21390 ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
21391 interfaceDesc, subtest->useDefaultName ? "default" : "custom",
21392 subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
21393 require_action( subtest->description, exit, err = kNoMemoryErr );
21394
21395 if( subtest->skipped )
21396 {
21397 subtest->startTime = NanoTimeGetCurrent();
21398 }
21399 else
21400 {
21401 // Generate a service name.
21402
21403 if( subtest->useDefaultName )
21404 {
21405 subtest->serviceName = inTest->computerName;
21406 }
21407 else
21408 {
21409 ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
21410 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21411 require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
21412
21413 subtest->serviceName = subtest->serviceNameCustom;
21414 }
21415
21416 // Generate a service type.
21417
21418 ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
21419 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21420 require_action( subtest->serviceType, exit, err = kNoMemoryErr );
21421
21422 subtest->serviceTypeLen = strlen( subtest->serviceType );
21423
21424 // Create SRV and TXT record name FQDN.
21425
21426 ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
21427 require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
21428
21429 // Generate a port number.
21430
21431 subtest->port = (uint16_t) RandomRange( 60000, 65535 );
21432
21433 // Generate TXT record data.
21434
21435 err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
21436 require_noerr( err, exit );
21437
21438 // Register service.
21439
21440 subtest->startTime = NanoTimeGetCurrent();
21441
21442 flags = kDNSServiceFlagsNoAutoRename;
21443 if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
21444 err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
21445 subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
21446 NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
21447 _RegistrationSubtestRegisterCallback, subtest );
21448 require_noerr( err, exit );
21449
21450 err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
21451 require_noerr( err, exit );
21452
21453 // Start timer.
21454
21455 check( !inTest->timer );
21456 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
21457 INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
21458 _RegistrationTestTimerHandler, inTest, &inTest->timer );
21459 require_noerr( err, exit );
21460 dispatch_resume( inTest->timer );
21461 }
21462
21463 *outSkipped = subtest->skipped;
21464
21465 check( !inTest->subtest );
21466 inTest->subtest = subtest;
21467 subtest = NULL;
21468
21469 exit:
21470 _RegistrationSubtestForget( &subtest );
21471 return( err );
21472 }
21473
21474 //===========================================================================================================================
21475
21476 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
21477 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
21478 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
21479 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
21480 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
21481 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
21482 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
21483 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
21484 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
21485 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
21486 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
21487 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
21488 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
21489 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
21490 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
21491 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
21492 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
21493 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
21494 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
21495 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
21496 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
21497 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
21498 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
21499 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
21500 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
21501 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
21502 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
21503 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
21504 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
21505 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
21506
21507 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
21508 #define kRegistrationTestResultType_Query CFSTR( "query" )
21509 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
21510 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
21511 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
21512
21513 static OSStatus
21514 _RegistrationTestAppendMissingResults(
21515 CFMutableArrayRef inMissingResults,
21516 const RegistrationResultTimes * inTimes,
21517 uint32_t inIfIndex,
21518 const char * inIfName );
21519
21520 static OSStatus _RegistrationTestEndSubtest( RegistrationTest *inTest )
21521 {
21522 OSStatus err;
21523 RegistrationSubtest * subtest;
21524 CFMutableDictionaryRef subtestReport;
21525 CFMutableArrayRef missing;
21526 char * txtStr;
21527 NanoTime64 now;
21528 Boolean subtestFailed;
21529 char startTime[ 32 ];
21530 char endTime[ 32 ];
21531 char ifNameBuf[ IF_NAMESIZE + 1 ];
21532
21533 now = NanoTimeGetCurrent();
21534
21535 subtest = inTest->subtest;
21536 inTest->subtest = NULL;
21537 _RegistrationSubtestStop( subtest );
21538
21539 missing = NULL;
21540 subtestReport = NULL;
21541 txtStr = NULL;
21542 if( subtest->txtPtr )
21543 {
21544 err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, &txtStr );
21545 require_noerr( err, exit );
21546 }
21547 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
21548 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
21549 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
21550 "{"
21551 "%kO=%s" // description
21552 "%kO=%s" // startTime
21553 "%kO=%s" // endTime
21554 "%kO=%s" // serviceFQDN
21555 "%kO=%lli" // ifIndex
21556 "%kO=%s" // ifName
21557 "%kO=%lli" // port
21558 "%kO=%s" // txt
21559 "%kO=%b" // registered
21560 "%kO=%b" // usedDefaultName
21561 "%kO=%b" // usedLODiscovery
21562 "}",
21563 kRegistrationTestReportKey_Description, subtest->description,
21564 kRegistrationTestReportKey_StartTime, startTime,
21565 kRegistrationTestReportKey_EndTime, endTime,
21566 kRegistrationTestReportKey_ServiceFQDN, subtest->serviceFQDN,
21567 kRegistrationTestReportKey_InterfaceIndex, (int64_t) subtest->ifIndex,
21568 kRegistrationTestReportKey_InterfaceName, if_indextoname( subtest->ifIndex, ifNameBuf ),
21569 kRegistrationTestReportKey_Port, (int64_t) subtest->port,
21570 kRegistrationTestReportKey_TXT, txtStr,
21571 kRegistrationTestReportKey_Registered, (int) subtest->registered,
21572 kRegistrationTestReportKey_UsedDefaultName, (int) subtest->useDefaultName,
21573 kRegistrationTestReportKey_UsedLODiscovery, (int) subtest->useLODiscovery );
21574 ForgetMem( &txtStr );
21575 require_noerr( err, exit );
21576
21577 if( !subtest->skipped && subtest->registered )
21578 {
21579 missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21580 require_action( missing, exit, err = kNoMemoryErr );
21581
21582 if( subtest->ifList )
21583 {
21584 RegistrationInterfaceItem * item;
21585
21586 for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
21587 {
21588 #if( TARGET_OS_WATCH )
21589 if( inTest->forBATS && item->base.isWiFi ) continue;
21590 #endif
21591 err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
21592 require_noerr( err, exit );
21593 }
21594 }
21595 else
21596 {
21597 err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
21598 require_noerr( err, exit );
21599 }
21600
21601 subtestFailed = false;
21602 if( CFArrayGetCount( missing ) > 0 )
21603 {
21604 subtestFailed = true;
21605 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
21606 }
21607 if( CFArrayGetCount( subtest->unexpected ) > 0 )
21608 {
21609 subtestFailed = true;
21610 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
21611 }
21612 #if( TARGET_OS_WATCH )
21613 if( CFArrayGetCount( subtest->ignored ) > 0 )
21614 {
21615 CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
21616 }
21617 #endif
21618 }
21619 else
21620 {
21621 subtestFailed = true;
21622 }
21623
21624 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
21625 if( subtestFailed )
21626 {
21627 CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
21628 if( !subtest->skipped ) inTest->failed = true;
21629 }
21630 CFArrayAppendValue( inTest->subtestReports, subtestReport );
21631
21632 exit:
21633 CFReleaseNullSafe( missing );
21634 CFReleaseNullSafe( subtestReport );
21635 _RegistrationSubtestFree( subtest );
21636 return( err );
21637 }
21638
21639 static OSStatus
21640 _RegistrationTestAppendMissingResult(
21641 CFMutableArrayRef inMissingResults,
21642 CFStringRef inType,
21643 uint32_t inIfIndex,
21644 const char * inIfName );
21645
21646 static OSStatus
21647 _RegistrationTestAppendMissingResults(
21648 CFMutableArrayRef inMissingResults,
21649 const RegistrationResultTimes * inTimes,
21650 uint32_t inIfIndex,
21651 const char * inIfName )
21652 {
21653 OSStatus err;
21654
21655 if( !inTimes->browseResultTime )
21656 {
21657 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
21658 inIfIndex, inIfName );
21659 require_noerr( err, exit );
21660 }
21661 if( !inTimes->querySRVResultTime )
21662 {
21663 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
21664 inIfIndex, inIfName );
21665 require_noerr( err, exit );
21666 }
21667 if( !inTimes->queryTXTResultTime )
21668 {
21669 err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
21670 inIfIndex, inIfName );
21671 require_noerr( err, exit );
21672 }
21673 err = kNoErr;
21674
21675 exit:
21676 return( err );
21677 }
21678
21679 static OSStatus
21680 _RegistrationTestAppendMissingResult(
21681 CFMutableArrayRef inMissingResults,
21682 CFStringRef inType,
21683 uint32_t inIfIndex,
21684 const char * inIfName )
21685 {
21686 OSStatus err;
21687 char ifName[ IF_NAMESIZE + 1 ];
21688
21689 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
21690 "{"
21691 "%kO=%O" // resultType
21692 "%kO=%lli" // ifIndex
21693 "%kO=%s" // ifName
21694 "}",
21695 kRegistrationTestReportKey_ResultType, inType,
21696 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
21697 kRegistrationTestReportKey_InterfaceName, inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
21698 return( err );
21699 }
21700
21701 //===========================================================================================================================
21702
21703 static void _RegistrationTestEnd( RegistrationTest *inTest )
21704 {
21705 OSStatus err;
21706 NanoTime64 now;
21707 CFPropertyListRef plist;
21708 char startTime[ 32 ];
21709 char endTime[ 32 ];
21710
21711 now = NanoTimeGetCurrent();
21712 _NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
21713 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
21714
21715 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
21716 "{"
21717 "%kO=%s" // startTime
21718 "%kO=%s" // endTime
21719 "%kO=%s" // computerName
21720 "%kO=%s" // localHostName
21721 "%kO=%O" // subtests
21722 "%kO=%b" // pass
21723 "}",
21724 kRegistrationTestReportKey_StartTime, startTime,
21725 kRegistrationTestReportKey_EndTime, endTime,
21726 kRegistrationTestReportKey_ComputerName, inTest->computerName,
21727 kRegistrationTestReportKey_LocalHostName, inTest->localHostName,
21728 kRegistrationTestReportKey_Subtests, inTest->subtestReports,
21729 kRegistrationTestReportKey_Pass, inTest->failed ? false : true );
21730 require_noerr( err, exit );
21731
21732 err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
21733 CFRelease( plist );
21734 require_noerr( err, exit );
21735
21736 exit:
21737 _RegistrationTestExit( inTest, err );
21738 }
21739
21740 //===========================================================================================================================
21741
21742 static void _RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
21743 {
21744 int exitCode;
21745
21746 if( inError )
21747 {
21748 FPrintF( stderr, "error: %#m\n", inError );
21749 exitCode = 1;
21750 }
21751 else
21752 {
21753 exitCode = inTest->failed ? 2 : 0;
21754 }
21755 _RegistrationTestForget( &inTest );
21756 exit( exitCode );
21757 }
21758
21759 //===========================================================================================================================
21760
21761 static OSStatus _RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
21762 {
21763 OSStatus err;
21764 RegistrationSubtest * obj;
21765
21766 obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
21767 require_action( obj, exit, err = kNoMemoryErr );
21768
21769 obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21770 require_action( obj->unexpected, exit, err = kNoMemoryErr );
21771
21772 #if( TARGET_OS_WATCH )
21773 obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21774 require_action( obj->ignored, exit, err = kNoMemoryErr );
21775 #endif
21776
21777 *outSubtest = obj;
21778 obj = NULL;
21779 err = kNoErr;
21780
21781 exit:
21782 if( obj ) _RegistrationSubtestFree( obj );
21783 return( err );
21784 }
21785
21786 //===========================================================================================================================
21787
21788 static void _RegistrationSubtestFree( RegistrationSubtest *inSubtest )
21789 {
21790 check( !inSubtest->registration );
21791 check( !inSubtest->browse );
21792 check( !inSubtest->querySRV );
21793 check( !inSubtest->queryTXT );
21794 check( !inSubtest->connection );
21795 ForgetMem( &inSubtest->serviceNameCustom );
21796 ForgetMem( &inSubtest->serviceType );
21797 ForgetMem( &inSubtest->serviceFQDN );
21798 ForgetMem( &inSubtest->txtPtr );
21799 ForgetCF( &inSubtest->unexpected );
21800 #if( TARGET_OS_WATCH )
21801 ForgetCF( &inSubtest->ignored );
21802 #endif
21803 _MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
21804 ForgetMem( &inSubtest->description );
21805 free( inSubtest );
21806 }
21807
21808 //===========================================================================================================================
21809
21810 static void _RegistrationSubtestStop( RegistrationSubtest *inSubtest )
21811 {
21812 DNSServiceForget( &inSubtest->registration );
21813 DNSServiceForget( &inSubtest->browse );
21814 DNSServiceForget( &inSubtest->querySRV );
21815 DNSServiceForget( &inSubtest->queryTXT );
21816 DNSServiceForget( &inSubtest->connection );
21817 }
21818
21819 //===========================================================================================================================
21820
21821 static OSStatus _RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
21822 {
21823 OSStatus err;
21824 RegistrationInterfaceItem * list;
21825 const MDNSInterfaceSubset subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
21826
21827 err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
21828 require_noerr( err, exit );
21829
21830 *outList = list;
21831
21832 exit:
21833 return( err );
21834 }
21835
21836 //===========================================================================================================================
21837
21838 static OSStatus
21839 _RegistrationTestCreateRandomTXTRecord(
21840 size_t inMinLen,
21841 size_t inMaxLen,
21842 uint8_t ** outTXTPtr,
21843 size_t * outTXTLen )
21844 {
21845 OSStatus err;
21846 uint8_t * ptr;
21847 const uint8_t * txtEnd;
21848 uint8_t * txtPtr = NULL;
21849 size_t txtLen;
21850
21851 require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
21852
21853 txtLen = RandomRange( inMinLen, inMaxLen );
21854 txtPtr = (uint8_t *) malloc( txtLen + 1 );
21855 require_action( txtPtr, exit, err = kNoMemoryErr );
21856
21857 _RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
21858
21859 ptr = txtPtr;
21860 txtEnd = &txtPtr[ txtLen ];
21861 while( ptr < txtEnd )
21862 {
21863 size_t maxLen, len;
21864
21865 maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
21866 len = RandomRange( 1, 255 );
21867 if( len > maxLen ) len = maxLen;
21868
21869 *ptr = (uint8_t) len;
21870 ptr += ( 1 + len );
21871 }
21872 check( ptr == txtEnd );
21873
21874 if( outTXTPtr )
21875 {
21876 *outTXTPtr = txtPtr;
21877 txtPtr = NULL;
21878 }
21879 if( outTXTLen ) *outTXTLen = txtLen;
21880 err = kNoErr;
21881
21882 exit:
21883 FreeNullSafe( txtPtr );
21884 return( err );
21885 }
21886
21887 //===========================================================================================================================
21888
21889 static void DNSSD_API
21890 _RegistrationSubtestRegisterCallback(
21891 DNSServiceRef inSDRef,
21892 DNSServiceFlags inFlags,
21893 DNSServiceErrorType inError,
21894 const char * inServiceName,
21895 const char * inServiceType,
21896 const char * inDomain,
21897 void * inContext )
21898 {
21899 OSStatus err;
21900 const NanoTime64 now = NanoTimeGetCurrent();
21901 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
21902
21903 Unused( inSDRef );
21904
21905 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
21906 ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
21907 _RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
21908 ( strcasecmp( inDomain, "local." ) == 0 ) )
21909 {
21910 if( !subtest->registered )
21911 {
21912 DNSServiceRef sdRef;
21913 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
21914
21915 subtest->registered = true;
21916
21917 // Create shared connection.
21918
21919 check( !subtest->connection );
21920 err = DNSServiceCreateConnection( &subtest->connection );
21921 require_noerr( err, exit );
21922
21923 err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
21924 require_noerr( err, exit );
21925
21926 // Start browse.
21927
21928 check( !subtest->browse );
21929 sdRef = subtest->connection;
21930 err = DNSServiceBrowse( &sdRef, flags,
21931 subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
21932 subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
21933 require_noerr( err, exit );
21934
21935 subtest->browse = sdRef;
21936 }
21937 }
21938 else
21939 {
21940 char timestamp[ 32 ];
21941
21942 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
21943 "{"
21944 "%kO=%O" // resultType
21945 "%kO=%s" // timestamp
21946 "%kO=%lli" // flags
21947 "%kO=%lli" // error
21948 "%kO=%s" // serviceName
21949 "%kO=%s" // serviceType
21950 "%kO=%s" // domain
21951 "}",
21952 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Registration,
21953 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
21954 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
21955 kRegistrationTestReportKey_Error, (int64_t) inError,
21956 kRegistrationTestReportKey_ServiceName, inServiceName,
21957 kRegistrationTestReportKey_ServiceType, inServiceType,
21958 kRegistrationTestReportKey_Domain, inDomain );
21959 require_noerr( err, exit );
21960 }
21961 err = kNoErr;
21962
21963 exit:
21964 if( err ) _RegistrationTestExit( subtest->test, err );
21965 }
21966
21967 //===========================================================================================================================
21968
21969 static void DNSSD_API
21970 _RegistrationSubtestBrowseCallback(
21971 DNSServiceRef inSDRef,
21972 DNSServiceFlags inFlags,
21973 uint32_t inIfIndex,
21974 DNSServiceErrorType inError,
21975 const char * inServiceName,
21976 const char * inServiceType,
21977 const char * inDomain,
21978 void * inContext )
21979 {
21980 OSStatus err;
21981 NanoTime64 now;
21982 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
21983 Boolean serviceIsCorrect, resultIsExpected;
21984
21985 Unused( inSDRef );
21986
21987 now = NanoTimeGetCurrent();
21988 if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
21989 _RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
21990 {
21991 serviceIsCorrect = true;
21992 }
21993 else
21994 {
21995 serviceIsCorrect = false;
21996 }
21997
21998 resultIsExpected = false;
21999 if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
22000 {
22001 RegistrationResultTimes * times;
22002
22003 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
22004 if( times )
22005 {
22006 DNSServiceRef sdRef;
22007 const DNSServiceFlags flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
22008 uint32_t ifIndex;
22009
22010 resultIsExpected = true;
22011 if( !times->browseResultTime ) times->browseResultTime = now;
22012
22013 ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
22014 if( !subtest->querySRV )
22015 {
22016 // Start SRV record query.
22017
22018 sdRef = subtest->connection;
22019 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
22020 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
22021 require_noerr( err, exit );
22022
22023 subtest->querySRV = sdRef;
22024 }
22025 if( !subtest->queryTXT )
22026 {
22027 // Start TXT record query.
22028
22029 sdRef = subtest->connection;
22030 err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
22031 kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
22032 require_noerr( err, exit );
22033
22034 subtest->queryTXT = sdRef;
22035 }
22036 }
22037 }
22038
22039 if( !resultIsExpected )
22040 {
22041 CFMutableArrayRef resultArray;
22042 char timestamp[ 32 ];
22043 const char * ifNamePtr;
22044 char ifNameBuf[ IF_NAMESIZE + 1 ];
22045
22046 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
22047 resultArray = subtest->unexpected;
22048 #if( TARGET_OS_WATCH )
22049 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
22050 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
22051 {
22052 resultArray = subtest->ignored;
22053 }
22054 #endif
22055 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
22056 "{"
22057 "%kO=%O" // resultType
22058 "%kO=%s" // timestamp
22059 "%kO=%lli" // flags
22060 "%kO=%lli" // ifIndex
22061 "%kO=%s" // ifName
22062 "%kO=%lli" // error
22063 "%kO=%s" // serviceName
22064 "%kO=%s" // serviceType
22065 "%kO=%s" // domain
22066 "}",
22067 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Browse,
22068 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
22069 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
22070 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
22071 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
22072 kRegistrationTestReportKey_Error, (int64_t) inError,
22073 kRegistrationTestReportKey_ServiceName, inServiceName,
22074 kRegistrationTestReportKey_ServiceType, inServiceType,
22075 kRegistrationTestReportKey_Domain, inDomain );
22076 require_noerr( err, exit );
22077 }
22078 err = kNoErr;
22079
22080 exit:
22081 if( err ) _RegistrationTestExit( subtest->test, err );
22082 }
22083
22084 //===========================================================================================================================
22085
22086 static Boolean
22087 _RegistrationSubtestIsSRVRecordDataValid(
22088 RegistrationSubtest * inSubtest,
22089 const uint8_t * inRDataPtr,
22090 size_t inRDataLen,
22091 Boolean inExpectRandHostname );
22092
22093 static void DNSSD_API
22094 _RegistrationSubtestQueryCallback(
22095 DNSServiceRef inSDRef,
22096 DNSServiceFlags inFlags,
22097 uint32_t inIfIndex,
22098 DNSServiceErrorType inError,
22099 const char * inName,
22100 uint16_t inType,
22101 uint16_t inClass,
22102 uint16_t inRDataLen,
22103 const void * inRDataPtr,
22104 uint32_t inTTL,
22105 void * inContext )
22106 {
22107 OSStatus err;
22108 NanoTime64 now;
22109 RegistrationSubtest * const subtest = (RegistrationSubtest *) inContext;
22110 Boolean resultIsExpected;
22111
22112 Unused( inSDRef );
22113 Unused( inTTL );
22114
22115 now = NanoTimeGetCurrent();
22116 resultIsExpected = false;
22117 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
22118 ( inClass == kDNSServiceClass_IN ) )
22119 {
22120 RegistrationResultTimes * times;
22121 Boolean isAWDL;
22122
22123 times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
22124 if( times )
22125 {
22126 if( inType == kDNSServiceType_SRV )
22127 {
22128 Boolean expectRandHostname;
22129
22130 if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
22131 {
22132 expectRandHostname = true;
22133 }
22134 else
22135 {
22136 expectRandHostname = false;
22137 }
22138 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
22139 {
22140 resultIsExpected = true;
22141 if( !times->querySRVResultTime ) times->querySRVResultTime = now;
22142 }
22143 }
22144 else if( inType == kDNSServiceType_TXT )
22145 {
22146 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
22147 {
22148 resultIsExpected = true;
22149 if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
22150 }
22151 }
22152 }
22153 }
22154
22155 if( !resultIsExpected )
22156 {
22157 CFMutableArrayRef resultArray;
22158 CFMutableDictionaryRef resultDict;
22159 CFStringRef rdataKey;
22160 char * rdataStr;
22161 const char * ifNamePtr;
22162 char timestamp[ 32 ];
22163 char ifNameBuf[ IF_NAMESIZE + 1 ];
22164
22165 ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
22166 resultArray = subtest->unexpected;
22167 #if( TARGET_OS_WATCH )
22168 if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
22169 ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
22170 ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
22171 {
22172 if( inType == kDNSServiceType_SRV )
22173 {
22174 const Boolean expectRandHostname = subtest->includeAWDL ? true : false;
22175
22176 if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
22177 {
22178 resultArray = subtest->ignored;
22179 }
22180 }
22181 else if( inType == kDNSServiceType_TXT )
22182 {
22183 if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
22184 {
22185 resultArray = subtest->ignored;
22186 }
22187 }
22188 }
22189 #endif
22190 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
22191 "{"
22192 "%kO=%O" // resultType
22193 "%kO=%s" // timestamp
22194 "%kO=%lli" // flags
22195 "%kO=%lli" // ifIndex
22196 "%kO=%s" // ifName
22197 "%kO=%lli" // error
22198 "%kO=%s" // serviceFQDN
22199 "%kO=%lli" // recordType
22200 "%kO=%lli" // class
22201 "}",
22202 kRegistrationTestReportKey_ResultType, kRegistrationTestResultType_Query,
22203 kRegistrationTestReportKey_Timestamp, _NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
22204 kRegistrationTestReportKey_Flags, (int64_t) inFlags,
22205 kRegistrationTestReportKey_InterfaceIndex, (int64_t) inIfIndex,
22206 kRegistrationTestReportKey_InterfaceName, ifNamePtr,
22207 kRegistrationTestReportKey_Error, (int64_t) inError,
22208 kRegistrationTestReportKey_ServiceFQDN, inName,
22209 kRegistrationTestReportKey_RecordType, (int64_t) inType,
22210 kRegistrationTestReportKey_RecordClass, (int64_t) inClass );
22211 require_noerr( err, exit );
22212
22213 rdataStr = NULL;
22214 DNSRecordDataToString( inRDataPtr, inRDataLen, inType, &rdataStr );
22215 if( rdataStr )
22216 {
22217 rdataKey = kRegistrationTestReportKey_RDataFormatted;
22218 }
22219 else
22220 {
22221 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
22222 require_action( rdataStr, exit, err = kNoMemoryErr );
22223
22224 rdataKey = kRegistrationTestReportKey_RDataHexString;
22225 }
22226 err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
22227 ForgetMem( &rdataStr );
22228 if( err ) CFRelease( resultDict );
22229 require_noerr( err, exit );
22230
22231 CFArrayAppendValue( resultArray, resultDict );
22232 CFRelease( resultDict );
22233 }
22234 err = kNoErr;
22235
22236 exit:
22237 if( err ) _RegistrationTestExit( subtest->test, err );
22238 }
22239
22240 static Boolean
22241 _RegistrationSubtestIsSRVRecordDataValid(
22242 RegistrationSubtest * inSubtest,
22243 const uint8_t * inRDataPtr,
22244 size_t inRDataLen,
22245 Boolean inExpectRandHostname )
22246 {
22247 const dns_fixed_fields_srv * fields;
22248 const uint8_t * const end = &inRDataPtr[ inRDataLen ];
22249 const uint8_t * label;
22250 size_t len;
22251 uint16_t port;
22252 Boolean isValid;
22253
22254 isValid = false;
22255 require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
22256
22257 fields = (const dns_fixed_fields_srv *) inRDataPtr;
22258 port = dns_fixed_fields_srv_get_port( fields );
22259 require_quiet( port == inSubtest->port, exit );
22260
22261 // First target label should be a UUID string for the AWDL interface.
22262
22263 label = (const uint8_t *) &fields[ 1 ];
22264 require_quiet( ( end - label ) >= 1, exit );
22265
22266 len = label[ 0 ];
22267 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
22268
22269 if( inExpectRandHostname )
22270 {
22271 if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
22272 }
22273 else
22274 {
22275 if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
22276 }
22277
22278 // Second target label should be "local".
22279
22280 label = &label[ 1 + len ];
22281 require_quiet( ( end - label ) >= 1, exit );
22282
22283 len = label[ 0 ];
22284 require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
22285
22286 if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
22287
22288 // Third target label should be the root label.
22289
22290 label = &label[ 1 + len ];
22291 require_quiet( ( end - label ) >= 1, exit );
22292
22293 len = label[ 0 ];
22294 if( len != 0 ) goto exit;
22295
22296 isValid = true;
22297
22298 exit:
22299 return( isValid );
22300 }
22301
22302 //===========================================================================================================================
22303
22304 static Boolean _RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
22305 {
22306 if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
22307 {
22308 const char * const ptr = &inServiceType[ inSubtest->serviceTypeLen ];
22309
22310 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
22311 }
22312 return( false );
22313 }
22314
22315 //===========================================================================================================================
22316
22317 static RegistrationResultTimes *
22318 _RegistrationSubtestGetInterfaceResultTimes(
22319 RegistrationSubtest * inSubtest,
22320 uint32_t inIfIndex,
22321 Boolean * outIsAWDL )
22322 {
22323 if( inSubtest->ifList )
22324 {
22325 RegistrationInterfaceItem * item;
22326
22327 for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
22328 {
22329 if( inIfIndex == item->base.ifIndex )
22330 {
22331 if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
22332 return( &item->times );
22333 }
22334 }
22335 }
22336 else
22337 {
22338 if( inIfIndex == inSubtest->ifIndex )
22339 {
22340 if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
22341 return( &inSubtest->ifTimes );
22342 }
22343 }
22344 return( NULL );
22345 }
22346
22347 //===========================================================================================================================
22348
22349 static void _RegistrationTestTimerHandler( void *inContext )
22350 {
22351 RegistrationTest * const test = (RegistrationTest *) inContext;
22352
22353 dispatch_source_forget( &test->timer );
22354 _RegistrationTestProceed( test );
22355 }
22356
22357 //===========================================================================================================================
22358
22359 #if( TARGET_OS_WATCH )
22360 static Boolean _RegistrationTestInterfaceIsWiFi( const char *inIfName )
22361 {
22362 NetTransportType type = kNetTransportType_Undefined;
22363
22364 SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
22365 return( ( type == kNetTransportType_WiFi ) ? true : false );
22366 }
22367 #endif
22368
22369 #if( TARGET_OS_DARWIN )
22370 //===========================================================================================================================
22371 // KeepAliveTestCmd
22372 //===========================================================================================================================
22373
22374 typedef enum
22375 {
22376 kKeepAliveCallVariant_Null = 0,
22377 kKeepAliveCallVariant_TakesSocket = 1, // DNSServiceSleepKeepalive(), which takes a connected socket.
22378 kKeepAliveCallVariant_TakesSockAddrs = 2, // DNSServiceSleepKeepalive_sockaddr(), which takes connection's sockaddrs.
22379
22380 } KeepAliveCallVariant;
22381
22382 typedef struct
22383 {
22384 int family; // TCP connection's address family.
22385 KeepAliveCallVariant callVariant; // Describes which DNSServiceSleepKeepalive* call to use.
22386 const char * description; // Subtest description.
22387
22388 } KeepAliveSubtestParams;
22389
22390 const KeepAliveSubtestParams kKeepAliveSubtestParams[] =
22391 {
22392 { AF_INET, kKeepAliveCallVariant_TakesSocket, "Calls DNSServiceSleepKeepalive() for IPv4 TCP connection." },
22393 { AF_INET, kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv4 TCP connection." },
22394 { AF_INET6, kKeepAliveCallVariant_TakesSocket, "Calls DNSServiceSleepKeepalive() for IPv6 TCP connection." },
22395 { AF_INET6, kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv6 TCP connection." }
22396 };
22397
22398 typedef struct
22399 {
22400 sockaddr_ip local; // TCP connection's local address and port.
22401 sockaddr_ip remote; // TCP connection's remote address and port.
22402 NanoTime64 startTime; // Subtest's start time.
22403 NanoTime64 endTime; // Subtest's end time.
22404 SocketRef clientSock; // Socket for client-side of TCP connection.
22405 SocketRef serverSock; // Socket for server-side of TCP connection.
22406 char * recordName; // Keepalive record's name.
22407 char * dataStr; // Data expected to be contained in keepalive record's data.
22408 const char * description; // Subtests's description.
22409 unsigned int timeoutKA; // Randomly-generated timeout value that gets put in keepalive record's rdata.
22410 OSStatus error; // Subtest's error.
22411
22412 } KeepAliveSubtest;
22413
22414 typedef struct KeepAliveTest * KeepAliveTestRef;
22415
22416 typedef struct
22417 {
22418 KeepAliveTestRef test; // Weak back pointer to test.
22419
22420 } KeepAliveTestConnectionContext;
22421
22422 struct KeepAliveTest
22423 {
22424 dispatch_queue_t queue; // Serial queue for test events.
22425 dispatch_semaphore_t doneSem; // Semaphore to signal when the test is done.
22426 dispatch_source_t readSource; // Read source for TCP listener socket.
22427 DNSServiceRef keepalive; // DNSServiceSleepKeepalive{,sockaddr} operation.
22428 DNSServiceRef query; // Query to verify registered keepalive record.
22429 dispatch_source_t timer; // Timer to put time limit on query.
22430 AsyncConnectionRef connection; // Establishes current subtest's TCP connection.
22431 KeepAliveTestConnectionContext * connectionCtx; // Weak pointer to connection's context.
22432 NanoTime64 startTime; // Test's start time.
22433 NanoTime64 endTime; // Test's end time.
22434 OSStatus error; // Test's error.
22435 size_t subtestIdx; // Index of current subtest.
22436 KeepAliveSubtest subtests[ 4 ]; // Subtest array.
22437 };
22438 check_compile_time( countof_field( struct KeepAliveTest, subtests ) == countof( kKeepAliveSubtestParams ) );
22439
22440 ulog_define_ex( kDNSSDUtilIdentifier, KeepAliveTest, kLogLevelInfo, kLogFlags_None, "KeepAliveTest", NULL );
22441 #define kat_ulog( LEVEL, ... ) ulog( &log_category_from_name( KeepAliveTest ), (LEVEL), __VA_ARGS__ )
22442
22443 static OSStatus _KeepAliveTestCreate( KeepAliveTestRef *outTest );
22444 static OSStatus _KeepAliveTestRun( KeepAliveTestRef inTest );
22445 static void _KeepAliveTestFree( KeepAliveTestRef inTest );
22446
22447 static void KeepAliveTestCmd( void )
22448 {
22449 OSStatus err;
22450 OutputFormatType outputFormat;
22451 KeepAliveTestRef test = NULL;
22452 CFPropertyListRef plist = NULL;
22453 CFMutableArrayRef subtests;
22454 size_t i;
22455 size_t subtestFailCount;
22456 Boolean testPassed = false;
22457 char startTime[ 32 ];
22458 char endTime[ 32 ];
22459
22460 err = OutputFormatFromArgString( gKeepAliveTest_OutputFormat, &outputFormat );
22461 require_noerr_quiet( err, exit );
22462
22463 err = _KeepAliveTestCreate( &test );
22464 require_noerr( err, exit );
22465
22466 err = _KeepAliveTestRun( test );
22467 require_noerr( err, exit );
22468
22469 _NanoTime64ToTimestamp( test->startTime, startTime, sizeof( startTime ) );
22470 _NanoTime64ToTimestamp( test->endTime, endTime, sizeof( endTime ) );
22471 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
22472 "{"
22473 "%kO=%s" // startTime
22474 "%kO=%s" // endTime
22475 "%kO=[%@]" // subtests
22476 "}",
22477 CFSTR( "startTime" ), startTime,
22478 CFSTR( "endTime" ), endTime,
22479 CFSTR( "subtests" ), &subtests );
22480 require_noerr( err, exit );
22481
22482 subtestFailCount = 0;
22483 check( test->subtestIdx == countof( test->subtests ) );
22484 for( i = 0; i < countof( test->subtests ); ++i )
22485 {
22486 KeepAliveSubtest * const subtest = &test->subtests[ i ];
22487 char errorDesc[ 128 ];
22488
22489 _NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
22490 _NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
22491 SNPrintF( errorDesc, sizeof( errorDesc ), "%m", subtest->error );
22492 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtests,
22493 "{"
22494 "%kO=%s" // startTime
22495 "%kO=%s" // endTime
22496 "%kO=%s" // description
22497 "%kO=%##a" // localAddr
22498 "%kO=%##a" // remoteAddr
22499 "%kO=%s" // recordName
22500 "%kO=%s" // expectedRData
22501 "%kO=" // error
22502 "{"
22503 "%kO=%lli" // code
22504 "%kO=%s" // description
22505 "}"
22506 "}",
22507 CFSTR( "startTime" ), startTime,
22508 CFSTR( "endTime" ), endTime,
22509 CFSTR( "description" ), subtest->description,
22510 CFSTR( "localAddr" ), &subtest->local.sa,
22511 CFSTR( "remoteAddr" ), &subtest->remote.sa,
22512 CFSTR( "recordName" ), subtest->recordName,
22513 CFSTR( "expectedRData" ), subtest->dataStr,
22514 CFSTR( "error" ),
22515 CFSTR( "code" ), (int64_t) subtest->error,
22516 CFSTR( "description" ), errorDesc
22517 );
22518 require_noerr( err, exit );
22519 if( subtest->error ) ++subtestFailCount;
22520 }
22521 if( subtestFailCount == 0 ) testPassed = true;
22522 CFPropertyListAppendFormatted( kCFAllocatorDefault, plist, "%kO=%b", CFSTR( "pass" ), testPassed );
22523
22524 err = OutputPropertyList( plist, outputFormat, gKeepAliveTest_OutputFilePath );
22525 require_noerr( err, exit );
22526
22527 exit:
22528 if( test ) _KeepAliveTestFree( test );
22529 CFReleaseNullSafe( plist );
22530 gExitCode = err ? 1 : ( testPassed ? 0 : 2 );
22531 }
22532
22533 //===========================================================================================================================
22534
22535 static void _KeepAliveTestStart( void *inCtx );
22536 static void _KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError );
22537 static OSStatus _KeepAliveTestStartSubtest( KeepAliveTestRef inTest );
22538 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest );
22539 static KeepAliveSubtest * _KeepAliveTestGetSubtest( KeepAliveTestRef inTest );
22540 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen );
22541 static OSStatus _KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone );
22542 static void _KeepAliveTestTCPAcceptHandler( void *inCtx );
22543 static void _KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
22544 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError );
22545 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest );
22546 static void DNSSD_API _KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inErr, void *inCtx );
22547 static void _KeepAliveTestQueryTimerHandler( void *inCtx );
22548 static void DNSSD_API
22549 _KeepAliveTestQueryRecordCallback(
22550 DNSServiceRef inSDRef,
22551 DNSServiceFlags inFlags,
22552 uint32_t inInterfaceIndex,
22553 DNSServiceErrorType inError,
22554 const char * inFullName,
22555 uint16_t inType,
22556 uint16_t inClass,
22557 uint16_t inRDataLen,
22558 const void * inRDataPtr,
22559 uint32_t inTTL,
22560 void * inCtx );
22561
22562 static OSStatus _KeepAliveTestCreate( KeepAliveTestRef *outTest )
22563 {
22564 OSStatus err;
22565 KeepAliveTestRef test;
22566 size_t i;
22567
22568 test = (KeepAliveTestRef) calloc( 1, sizeof( *test ) );
22569 require_action( test, exit, err = kNoMemoryErr );
22570
22571 test->error = kInProgressErr;
22572 for( i = 0; i < countof( test->subtests ); ++i )
22573 {
22574 KeepAliveSubtest * const subtest = &test->subtests[ i ];
22575
22576 subtest->local.sa.sa_family = AF_UNSPEC;
22577 subtest->remote.sa.sa_family = AF_UNSPEC;
22578 subtest->clientSock = kInvalidSocketRef;
22579 subtest->serverSock = kInvalidSocketRef;
22580 }
22581 test->queue = dispatch_queue_create( "com.apple.dnssdutil.keepalive-test", DISPATCH_QUEUE_SERIAL );
22582 require_action( test->queue, exit, err = kNoResourcesErr );
22583
22584 test->doneSem = dispatch_semaphore_create( 0 );
22585 require_action( test->doneSem, exit, err = kNoResourcesErr );
22586
22587 *outTest = test;
22588 test = NULL;
22589 err = kNoErr;
22590
22591 exit:
22592 if( test ) _KeepAliveTestFree( test );
22593 return( err );
22594 }
22595
22596 //===========================================================================================================================
22597
22598 static OSStatus _KeepAliveTestRun( KeepAliveTestRef inTest )
22599 {
22600 dispatch_async_f( inTest->queue, inTest, _KeepAliveTestStart );
22601 dispatch_semaphore_wait( inTest->doneSem, DISPATCH_TIME_FOREVER );
22602 return( inTest->error );
22603 }
22604
22605 //===========================================================================================================================
22606
22607 static void _KeepAliveTestFree( KeepAliveTestRef inTest )
22608 {
22609 size_t i;
22610
22611 check( !inTest->readSource );
22612 check( !inTest->query );
22613 check( !inTest->timer );
22614 check( !inTest->keepalive );
22615 check( !inTest->connection );
22616 check( !inTest->connectionCtx );
22617 dispatch_forget( &inTest->queue );
22618 dispatch_forget( &inTest->doneSem );
22619 for( i = 0; i < countof( inTest->subtests ); ++i )
22620 {
22621 KeepAliveSubtest * const subtest = &inTest->subtests[ i ];
22622
22623 check( !IsValidSocket( subtest->clientSock ) );
22624 check( !IsValidSocket( subtest->serverSock ) );
22625 ForgetMem( &subtest->recordName );
22626 ForgetMem( &subtest->dataStr );
22627 }
22628 free( inTest );
22629 }
22630
22631 //===========================================================================================================================
22632
22633 static void _KeepAliveTestStart( void *inCtx )
22634 {
22635 OSStatus err;
22636 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
22637
22638 test->error = kInProgressErr;
22639 test->startTime = NanoTimeGetCurrent();
22640 err = _KeepAliveTestStartSubtest( test );
22641 require_noerr( err, exit );
22642
22643 exit:
22644 if( err ) _KeepAliveTestStop( test, err );
22645 }
22646
22647 //===========================================================================================================================
22648
22649 static void _KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError )
22650 {
22651 size_t i;
22652
22653 inTest->error = inError;
22654 inTest->endTime = NanoTimeGetCurrent();
22655 _KeepAliveTestStopSubtest( inTest );
22656 for( i = 0; i < countof( inTest->subtests ); ++i )
22657 {
22658 KeepAliveSubtest * const subtest = &inTest->subtests[ i ];
22659
22660 ForgetSocket( &subtest->clientSock );
22661 ForgetSocket( &subtest->serverSock );
22662 }
22663 dispatch_semaphore_signal( inTest->doneSem );
22664 }
22665
22666 //===========================================================================================================================
22667
22668 static OSStatus _KeepAliveTestStartSubtest( KeepAliveTestRef inTest )
22669 {
22670 OSStatus err;
22671 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( inTest );
22672 const KeepAliveSubtestParams * const params = &kKeepAliveSubtestParams[ inTest->subtestIdx ];
22673 int port;
22674 SocketRef sock = kInvalidSocketRef;
22675 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
22676 SocketContext * sockCtx = NULL;
22677 KeepAliveTestConnectionContext * cnxCtx = NULL;
22678 Boolean useIPv4;
22679 char serverAddrStr[ 64 ];
22680 char prefix[ 64 ];
22681
22682 subtest->error = kInProgressErr;
22683 subtest->startTime = NanoTimeGetCurrent();
22684 subtest->description = params->description;
22685
22686 require_action( ( params->family == AF_INET ) || ( params->family == AF_INET6 ), exit, err = kInternalErr );
22687
22688 // Create TCP listener socket.
22689
22690 useIPv4 = ( params->family == AF_INET ) ? true : false;
22691 err = ServerSocketOpenEx( params->family, SOCK_STREAM, IPPROTO_TCP,
22692 useIPv4 ? ( (const void *) &loopbackV4 ) : ( (const void *) &in6addr_loopback ), kSocketPort_Auto, &port,
22693 kSocketBufferSize_DontSet, &sock );
22694 require_noerr( err, exit );
22695
22696 if( useIPv4 ) SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "%.4a:%d", &loopbackV4, port );
22697 else SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "[%.16a]:%d", in6addr_loopback.s6_addr, port );
22698 _KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
22699 kat_ulog( kLogLevelInfo, "%s: Will listen for connections on %s\n", prefix, serverAddrStr );
22700
22701 sockCtx = SocketContextCreate( sock, inTest, &err );
22702 require_noerr( err, exit );
22703 sock = kInvalidSocketRef;
22704
22705 // Create read source for TCP listener socket.
22706
22707 check( !inTest->readSource );
22708 err = DispatchReadSourceCreate( sockCtx->sock, inTest->queue, _KeepAliveTestTCPAcceptHandler,
22709 SocketContextCancelHandler, sockCtx, &inTest->readSource );
22710 require_noerr( err, exit );
22711 sockCtx = NULL;
22712 dispatch_resume( inTest->readSource );
22713
22714 cnxCtx = (KeepAliveTestConnectionContext *) calloc( 1, sizeof( *cnxCtx ) );
22715 require_action( cnxCtx, exit, err = kNoMemoryErr );
22716
22717 // Start asynchronous connection to listener socket.
22718
22719 kat_ulog( kLogLevelInfo, "%s: Will connect to %s\n", prefix, serverAddrStr );
22720
22721 check( !inTest->connection );
22722 err = AsyncConnection_Connect( &inTest->connection, serverAddrStr, 0, kAsyncConnectionFlags_None,
22723 5 * UINT64_C_safe( kNanosecondsPerSecond ), kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
22724 NULL, NULL, _KeepAliveTestConnectionHandler, cnxCtx, inTest->queue );
22725 require_noerr( err, exit );
22726
22727 cnxCtx->test = inTest;
22728 check( !inTest->connectionCtx );
22729 inTest->connectionCtx = cnxCtx;
22730 cnxCtx = NULL;
22731
22732 exit:
22733 ForgetSocket( &sock );
22734 if( sockCtx ) SocketContextRelease( sockCtx );
22735 FreeNullSafe( cnxCtx );
22736 return( err );
22737 }
22738
22739 //===========================================================================================================================
22740
22741 static void _KeepAliveTestStopSubtest( KeepAliveTestRef inTest )
22742 {
22743 dispatch_source_forget( &inTest->readSource );
22744 DNSServiceForget( &inTest->keepalive );
22745 DNSServiceForget( &inTest->query );
22746 dispatch_source_forget( &inTest->timer );
22747 _KeepAliveTestForgetConnection( inTest );
22748 }
22749
22750 //===========================================================================================================================
22751
22752 static KeepAliveSubtest * _KeepAliveTestGetSubtest( KeepAliveTestRef inTest )
22753 {
22754 return( ( inTest->subtestIdx < countof( inTest->subtests ) ) ? &inTest->subtests[ inTest->subtestIdx ] : NULL );
22755 }
22756
22757 //===========================================================================================================================
22758
22759 static const char * _KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen )
22760 {
22761 SNPrintF( inBufPtr, inBufLen, "Subtest %zu/%zu", inTest->subtestIdx + 1, countof( inTest->subtests ) );
22762 return( inBufPtr );
22763 }
22764
22765 //===========================================================================================================================
22766
22767 static OSStatus _KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone )
22768 {
22769 OSStatus err;
22770 KeepAliveSubtest * subtest;
22771
22772 require_action( inTest->subtestIdx <= countof( inTest->subtests ), exit, err = kInternalErr );
22773
22774 if( inTest->subtestIdx < countof( inTest->subtests ) )
22775 {
22776 subtest = _KeepAliveTestGetSubtest( inTest );
22777 _KeepAliveTestStopSubtest( inTest );
22778 subtest->endTime = NanoTimeGetCurrent();
22779 subtest->error = inSubtestError;
22780 if( ++inTest->subtestIdx < countof( inTest->subtests ) )
22781 {
22782 err = _KeepAliveTestStartSubtest( inTest );
22783 require_noerr_quiet( err, exit );
22784 }
22785 }
22786 err = kNoErr;
22787
22788 exit:
22789 if( outDone ) *outDone = ( !err && ( inTest->subtestIdx == countof( inTest->subtests ) ) ) ? true : false;
22790 return( err );
22791 }
22792
22793 //===========================================================================================================================
22794
22795 static void _KeepAliveTestTCPAcceptHandler( void *inCtx )
22796 {
22797 OSStatus err;
22798 const SocketContext * const sockCtx = (SocketContext *) inCtx;
22799 const KeepAliveTestRef test = (KeepAliveTestRef) sockCtx->userContext;
22800 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
22801 sockaddr_ip peer;
22802 socklen_t len;
22803 char prefix[ 64 ];
22804
22805 check( !IsValidSocket( subtest->serverSock ) );
22806 len = (socklen_t) sizeof( peer );
22807 subtest->serverSock = accept( sockCtx->sock, &peer.sa, &len );
22808 err = map_socket_creation_errno( subtest->serverSock );
22809 require_noerr( err, exit );
22810
22811 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
22812 kat_ulog( kLogLevelInfo, "%s: Accepted connection from %##a\n", prefix, &peer.sa );
22813
22814 dispatch_source_forget( &test->readSource );
22815
22816 exit:
22817 if( err ) _KeepAliveTestStop( test, err );
22818 }
22819
22820 //===========================================================================================================================
22821
22822 static void _KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
22823 {
22824 KeepAliveTestConnectionContext * ctx = (KeepAliveTestConnectionContext *) inArg;
22825 const KeepAliveTestRef test = ctx->test;
22826
22827 if( test )
22828 {
22829 _KeepAliveTestForgetConnection( test );
22830 _KeepAliveTestHandleConnection( test, inSock, inError );
22831 inSock = kInvalidSocketRef;
22832 }
22833 ForgetSocket( &inSock );
22834 free( ctx );
22835 }
22836
22837 //===========================================================================================================================
22838
22839 #define kKeepAliveTestQueryTimeoutSecs 5
22840
22841 static void _KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError )
22842 {
22843 OSStatus err;
22844 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( inTest );
22845 const KeepAliveSubtestParams * const params = &kKeepAliveSubtestParams[ inTest->subtestIdx ];
22846 socklen_t len;
22847 uint32_t value;
22848 int family, i;
22849 Boolean subtestFailed = false;
22850 Boolean done;
22851 char prefix[ 64 ];
22852
22853 require_noerr_action( inError, exit, err = inError );
22854
22855 check( !IsValidSocket( subtest->clientSock ) );
22856 subtest->clientSock = inSock;
22857 inSock = kInvalidSocketRef;
22858
22859 // Get local and remote IP addresses.
22860
22861 len = (socklen_t) sizeof( subtest->local );
22862 err = getsockname( subtest->clientSock, &subtest->local.sa, &len );
22863 err = map_global_noerr_errno( err );
22864 require_noerr( err, exit );
22865
22866 len = (socklen_t) sizeof( subtest->remote );
22867 err = getpeername( subtest->clientSock, &subtest->remote.sa, &len );
22868 err = map_global_noerr_errno( err );
22869 require_noerr( err, exit );
22870
22871 _KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
22872 kat_ulog( kLogLevelInfo, "%s: Connection established: %##a <-> %##a\n",
22873 prefix, &subtest->local.sa, &subtest->remote.sa );
22874
22875 // Call either DNSServiceSleepKeepalive() or DNSServiceSleepKeepalive_sockaddr().
22876
22877 check( subtest->timeoutKA == 0 );
22878 subtest->timeoutKA = (unsigned int) RandomRange( 1, UINT_MAX );
22879
22880 switch( params->callVariant )
22881 {
22882 case kKeepAliveCallVariant_TakesSocket:
22883 kat_ulog( kLogLevelInfo, "%s: Will call DNSServiceSleepKeepalive() for client-side socket\n", prefix );
22884 check( !inTest->keepalive );
22885 err = DNSServiceSleepKeepalive( &inTest->keepalive, 0, subtest->clientSock,
22886 subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
22887 require_noerr( err, exit );
22888
22889 err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
22890 require_noerr( err, exit );
22891 break;
22892
22893 case kKeepAliveCallVariant_TakesSockAddrs:
22894 kat_ulog( kLogLevelInfo,
22895 "%s: Will call DNSServiceSleepKeepalive_sockaddr() for local and remote sockaddrs\n", prefix );
22896 check( !inTest->keepalive );
22897 if( __builtin_available( macOS 10.15.4, iOS 13.2.2, watchOS 6.2, tvOS 13.2, * ) )
22898 {
22899 err = DNSServiceSleepKeepalive_sockaddr( &inTest->keepalive, 0, &subtest->local.sa, &subtest->remote.sa,
22900 subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
22901 require_noerr( err, exit );
22902 }
22903 else
22904 {
22905 kat_ulog( kLogLevelError, "DNSServiceSleepKeepalive_sockaddr() is not available on this OS.\n" );
22906 subtestFailed = true;
22907 err = kUnsupportedErr;
22908 goto exit;
22909 }
22910 err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
22911 require_noerr( err, exit );
22912 break;
22913
22914 default:
22915 kat_ulog( kLogLevelError, "%s: Invalid KeepAliveCallVariant value %d\n", prefix, (int) params->callVariant );
22916 err = kInternalErr;
22917 goto exit;
22918 }
22919 // Use the same logic that the DNSServiceSleepKeepalive functions use to derive a record name and rdata.
22920
22921 value = 0;
22922 family = subtest->local.sa.sa_family;
22923 if( family == AF_INET )
22924 {
22925 const struct sockaddr_in * const sin = &subtest->local.v4;
22926 const uint8_t * ptr;
22927
22928 check_compile_time_code( sizeof( sin->sin_addr.s_addr ) == 4 );
22929 ptr = (const uint8_t *) &sin->sin_addr.s_addr;
22930 for( i = 0; i < 4; ++i ) value += ptr[ i ];
22931 value += sin->sin_port; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
22932
22933 check( subtest->remote.sa.sa_family == AF_INET );
22934 ASPrintF( &subtest->dataStr, "t=%u h=%.4a d=%.4a l=%u r=%u",
22935 subtest->timeoutKA, &subtest->local.v4.sin_addr.s_addr, &subtest->remote.v4.sin_addr.s_addr,
22936 ntohs( subtest->local.v4.sin_port ), ntohs( subtest->remote.v4.sin_port ) );
22937 require_action( subtest->dataStr, exit, err = kNoMemoryErr );
22938 }
22939 else if( family == AF_INET6 )
22940 {
22941 const struct sockaddr_in6 * const sin6 = &subtest->local.v6;
22942
22943 check_compile_time_code( countof( sin6->sin6_addr.s6_addr ) == 16 );
22944 for( i = 0; i < 16; ++i ) value += sin6->sin6_addr.s6_addr[ i ];
22945 value += sin6->sin6_port; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
22946
22947 check( subtest->remote.sa.sa_family == AF_INET6 );
22948 ASPrintF( &subtest->dataStr, "t=%u H=%.16a D=%.16a l=%u r=%u",
22949 subtest->timeoutKA, subtest->local.v6.sin6_addr.s6_addr, subtest->remote.v6.sin6_addr.s6_addr,
22950 ntohs( subtest->local.v6.sin6_port ), ntohs( subtest->remote.v6.sin6_port ) );
22951 require_action( subtest->dataStr, exit, err = kNoMemoryErr );
22952 }
22953 else
22954 {
22955 kat_ulog( kLogLevelError, "%s: Unexpected local address family %d\n", prefix, family );
22956 err = kInternalErr;
22957 goto exit;
22958 }
22959
22960 // Start query for the new keepalive record.
22961
22962 check( !subtest->recordName );
22963 ASPrintF( &subtest->recordName, "%u._keepalive._dns-sd._udp.local.", value );
22964 require_action( subtest->recordName, exit, err = kNoMemoryErr );
22965
22966 kat_ulog( kLogLevelInfo, "%s: Will query for %s NULL record\n", prefix, subtest->recordName );
22967 check( !inTest->query );
22968 err = DNSServiceQueryRecord( &inTest->query, 0, kDNSServiceInterfaceIndexLocalOnly, subtest->recordName,
22969 kDNSServiceType_NULL, kDNSServiceClass_IN, _KeepAliveTestQueryRecordCallback, inTest );
22970 require_noerr( err, exit );
22971
22972 err = DNSServiceSetDispatchQueue( inTest->query, inTest->queue );
22973 require_noerr( err, exit );
22974
22975 // Start timer to enforce a time limit on the query.
22976
22977 check( !inTest->timer );
22978 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kKeepAliveTestQueryTimeoutSecs ),
22979 kKeepAliveTestQueryTimeoutSecs * ( INT64_C_safe( kNanosecondsPerSecond ) / 20 ), inTest->queue,
22980 _KeepAliveTestQueryTimerHandler, inTest, &inTest->timer );
22981 require_noerr( err, exit );
22982 dispatch_resume( inTest->timer );
22983
22984 exit:
22985 ForgetSocket( &inSock );
22986 if( subtestFailed )
22987 {
22988 err = _KeepAliveTestContinue( inTest, err, &done );
22989 check_noerr( err );
22990 }
22991 else
22992 {
22993 done = false;
22994 }
22995 if( err || done ) _KeepAliveTestStop( inTest, err );
22996 }
22997
22998 //===========================================================================================================================
22999
23000 static void _KeepAliveTestForgetConnection( KeepAliveTestRef inTest )
23001 {
23002 if( inTest->connection )
23003 {
23004 check( inTest->connectionCtx );
23005 inTest->connectionCtx->test = NULL; // Unset the connection's back pointer to test.
23006 inTest->connectionCtx = NULL; // Context will be freed by the connection's handler.
23007 AsyncConnection_Forget( &inTest->connection );
23008 }
23009 }
23010
23011 //===========================================================================================================================
23012
23013 static void DNSSD_API _KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
23014 {
23015 OSStatus err;
23016 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
23017 char prefix[ 64 ];
23018
23019 Unused( inSDRef );
23020
23021 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23022 kat_ulog( kLogLevelInfo, "%s: Keepalive callback error: %#m\n", prefix, inError );
23023
23024 if( inError )
23025 {
23026 Boolean done;
23027
23028 err = _KeepAliveTestContinue( test, inError, &done );
23029 check_noerr( err );
23030 if( err || done ) _KeepAliveTestStop( test, err );
23031 }
23032 }
23033
23034 //===========================================================================================================================
23035
23036 static void _KeepAliveTestQueryTimerHandler( void *inCtx )
23037 {
23038 OSStatus err;
23039 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
23040 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
23041 Boolean done;
23042 char prefix[ 64 ];
23043
23044 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23045 kat_ulog( kLogLevelInfo, "%s: Query for \"%s\" timed out.\n", prefix, subtest->recordName );
23046
23047 err = _KeepAliveTestContinue( test, kTimeoutErr, &done );
23048 check_noerr( err );
23049 if( err || done ) _KeepAliveTestStop( test, err );
23050 }
23051
23052 //===========================================================================================================================
23053
23054 static void DNSSD_API
23055 _KeepAliveTestQueryRecordCallback(
23056 DNSServiceRef inSDRef,
23057 DNSServiceFlags inFlags,
23058 uint32_t inInterfaceIndex,
23059 DNSServiceErrorType inError,
23060 const char * inFullName,
23061 uint16_t inType,
23062 uint16_t inClass,
23063 uint16_t inRDataLen,
23064 const void * inRDataPtr,
23065 uint32_t inTTL,
23066 void * inCtx )
23067 {
23068 OSStatus err;
23069 const KeepAliveTestRef test = (KeepAliveTestRef) inCtx;
23070 KeepAliveSubtest * const subtest = _KeepAliveTestGetSubtest( test );
23071 const uint8_t * ptr;
23072 size_t dataStrLen, minLen;
23073 Boolean done;
23074 char prefix[ 64 ];
23075
23076 Unused( inSDRef );
23077 Unused( inInterfaceIndex );
23078 Unused( inTTL );
23079
23080 _KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23081 if( strcasecmp( inFullName, subtest->recordName ) != 0 )
23082 {
23083 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record name \"%s\".\n",
23084 prefix, subtest->recordName, inFullName );
23085 err = kUnexpectedErr;
23086 goto exit;
23087 }
23088 if( inType != kDNSServiceType_NULL )
23089 {
23090 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record type %d (%s) != %d (NULL).\n",
23091 prefix, subtest->recordName, inType, RecordTypeToString( inType ), kDNSServiceType_NULL );
23092 err = kUnexpectedErr;
23093 goto exit;
23094 }
23095 if( inClass != kDNSServiceClass_IN )
23096 {
23097 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record class %d != %d (IN).\n",
23098 prefix, subtest->recordName, inClass, kDNSServiceClass_IN );
23099 err = kUnexpectedErr;
23100 goto exit;
23101 }
23102 if( inError )
23103 {
23104 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected error %#m.\n",
23105 prefix, subtest->recordName, inError );
23106 err = inError;
23107 goto exit;
23108 }
23109 if( ( inFlags & kDNSServiceFlagsAdd ) == 0 )
23110 {
23111 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Missing Add flag.\n", prefix, subtest->recordName );
23112 err = kUnexpectedErr;
23113 goto exit;
23114 }
23115 kat_ulog( kLogLevelInfo, "%s: QueryRecord(%s) result rdata: %#H\n",
23116 prefix, subtest->recordName, inRDataPtr, inRDataLen, inRDataLen );
23117
23118 dataStrLen = strlen( subtest->dataStr ) + 1; // There's a NUL terminator at the end of the rdata.
23119 minLen = 1 + dataStrLen; // The first byte of the rdata is a length byte.
23120 if( inRDataLen < minLen )
23121 {
23122 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata length (%d) is less than expected minimum (%zu).\n",
23123 prefix, subtest->recordName, inRDataLen, minLen );
23124 err = kUnexpectedErr;
23125 goto exit;
23126 }
23127 ptr = (const uint8_t *) inRDataPtr;
23128 if( ptr[ 0 ] < dataStrLen )
23129 {
23130 kat_ulog( kLogLevelError,
23131 "%s: QueryRecord(%s) result: rdata length byte value (%d) is less than expected minimum (%zu).\n",
23132 prefix, subtest->recordName, ptr[ 0 ], dataStrLen );
23133 err = kUnexpectedErr;
23134 goto exit;
23135 }
23136 if( memcmp( &ptr[ 1 ], subtest->dataStr, dataStrLen - 1 ) != 0 )
23137 {
23138 kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata body doesn't contain '%s'.\n",
23139 prefix, subtest->recordName, subtest->dataStr );
23140 }
23141 err = kNoErr;
23142
23143 exit:
23144 err = _KeepAliveTestContinue( test, err, &done );
23145 check_noerr( err );
23146 if( err || done ) _KeepAliveTestStop( test, kNoErr );
23147 }
23148 #endif // TARGET_OS_DARWIN
23149
23150 #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
23151 //===========================================================================================================================
23152 // DNSSECTestCmd
23153 //===========================================================================================================================
23154
23155 #define kDNSSECTestQueryTimeoutSecs 4
23156
23157 typedef struct DNSSECTest_BasicValidationContext
23158 {
23159 uint32_t number_of_answers; // The number of answers expected to receive in the "basic validation" test
23160
23161 } DNSSECTest_BasicValidationContext;
23162
23163 //===========================================================================================================================
23164
23165 typedef struct
23166 {
23167 DNSServiceRef query;
23168 dispatch_source_t queryTimer; // Used to setup timeout timer, to prevent from waiting forever.
23169 Boolean testStarted;
23170 const char * testName; // The name of the curreent running test case.
23171 const char * testCaseName; // The query name in the subtest of the test case.
23172 union {
23173 DNSSECTest_BasicValidationContext basicValidation;
23174 } testCaseContext; // Contains different customized context pointer, which can be used by different test cases.
23175 const char * subtestQueryName; // The query name that is passed to mDNSResponder API
23176 int subtestIndex; // The index of the current case in the test input array.
23177 pid_t localServerPID; // The pid of the dnssdutil server
23178 NanoTime64 startTime; // The time when the DNSSEC test case starts.
23179 CFMutableArrayRef subtestReport; // The reference to the CFMutableArrayRef, which contains subtest reports for different subtests
23180 NanoTime64 subtestStartTime; // The time when the subtest starts
23181 Boolean subtestFailed; // Indicate if any subtest failed before.
23182
23183 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
23184 OutputFormatType outputFormat; // Format of test report output.
23185 } DNSSECTestContext;
23186
23187 //===========================================================================================================================
23188
23189 typedef struct
23190 {
23191 const char * testCaseName; // The name of the test case that will be run.
23192 void (*testCaseHandler)( DNSSECTestContext * context ); // The main function of the test case.
23193 } DNSSECTestTestCase;
23194
23195 //===========================================================================================================================
23196
23197 static void DNSSECTestSetupLocalDNSServer( DNSSECTestContext *context );
23198 static void DNSSECTestStartTestCase( DNSSECTestContext *context );
23199
23200 //===========================================================================================================================
23201
23202 // DNSSEC test cases
23203 static void DNSSECTest_BasicValidation( DNSSECTestContext *context );
23204
23205 static DNSSECTestTestCase DNSSECTestTestCases[] =
23206 {
23207 { "basic validation", DNSSECTest_BasicValidation }
23208 };
23209
23210 //===========================================================================================================================
23211
23212 // The main function to start the DNSSEC test
23213 static void
23214 DNSSECTestCmd( void )
23215 {
23216 OSStatus err;
23217 dispatch_source_t signalSource = NULL;
23218 DNSSECTestContext * context = NULL;
23219
23220 // Set up SIGINT handler.
23221 signal( SIGINT, SIG_IGN );
23222 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
23223 require_noerr( err, exit );
23224 dispatch_resume( signalSource );
23225
23226 // Create the test context.
23227 context = (DNSSECTestContext *) calloc( 1, sizeof( *context ) );
23228 require_action( context, exit, err = kNoMemoryErr );
23229
23230 context->testStarted = false;
23231
23232 // Get the command line option.
23233 context->testCaseName = strdup( gDNSSECTest_TestCaseName );
23234 require( context->testCaseName , exit );
23235
23236 // Start the subtest from index 0
23237 context->subtestIndex = 0;
23238
23239 // Get the output format.
23240 err = OutputFormatFromArgString( gDNSSECTest_OutputFormat, &context->outputFormat );
23241 require_noerr_quiet( err, exit );
23242
23243 // Get the output file path.
23244 if( gDNSSECTest_OutputFilePath )
23245 {
23246 context->outputFilePath = strdup( gDNSSECTest_OutputFilePath );
23247 require_noerr_quiet( context->outputFilePath , exit );
23248 }
23249
23250 // Initialize the CFArray to store the test result.
23251 context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
23252 context->startTime = NanoTimeGetCurrent();
23253
23254 // Start the local dnssdutil server.
23255 DNSSECTestSetupLocalDNSServer( context );
23256
23257 // Start the test.
23258 DNSSECTestStartTestCase( context );
23259
23260 exit:
23261 exit( 1 );
23262 }
23263
23264 //===========================================================================================================================
23265
23266 // Start the local DNS server with dnssdutil server command.
23267 static void
23268 DNSSECTestSetupLocalDNSServer( DNSSECTestContext *context )
23269 {
23270 Unused( context );
23271 pid_t pid = getpid();
23272
23273 OSStatus err = _SpawnCommand( &context->localServerPID, NULL, NULL, "dnssdutil server --loopback --follow %lld",
23274 (int64_t) pid );
23275 require_noerr_action( err, exit,
23276 FPrintF( stderr, "dnssdutil server --loopback --follow %lld failed, error: %d\n", (int64_t) pid, err ) );
23277
23278 // Wait long enough to allow the DNS server being setup.
23279 sleep( 2 );
23280
23281 return;
23282 exit:
23283 exit( 1 );
23284 }
23285
23286 //===========================================================================================================================
23287
23288 // Start the test case that user specifies.
23289 static void
23290 DNSSECTestStartTestCase( DNSSECTestContext *context )
23291 {
23292 Boolean findTestCase = false;
23293
23294 for ( int i = 0; i < (int)countof( DNSSECTestTestCases ); i++ )
23295 {
23296 if( strcmp( context->testCaseName, DNSSECTestTestCases[i].testCaseName ) == 0 )
23297 {
23298 DNSSECTestTestCases[ i ].testCaseHandler( context );
23299 findTestCase = true;
23300 }
23301 }
23302
23303 if( !findTestCase )
23304 {
23305 FPrintF( stdout, "Unknown test case \"%s\"\n", context->testCaseName );
23306 exit( 1 );
23307 }
23308 }
23309
23310 //===========================================================================================================================
23311
23312 // Write the current subtest status into the report list. When this function is called, either some error occurs or the
23313 // subtest finishes.
23314 static void
23315 _DNSSECTestQueryWriteSubtestReport( DNSSECTestContext *inContext, const char *errorDescription )
23316 {
23317 OSStatus err;
23318 NanoTime64 now;
23319 char startTime[ 32 ];
23320 char endTime[ 32 ];
23321
23322 now = NanoTimeGetCurrent();
23323 _NanoTime64ToTimestamp( inContext->subtestStartTime, startTime, sizeof( startTime ) );
23324 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
23325
23326 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->subtestReport,
23327 "{"
23328 "%kO=%s"
23329 "%kO=%s"
23330 "%kO=%s"
23331 "%kO=%s"
23332 "%kO=%s"
23333 "}",
23334 CFSTR( "Start Time" ), startTime,
23335 CFSTR( "End Time" ), endTime,
23336 CFSTR( "Subtest Query Name" ), inContext->subtestQueryName,
23337 CFSTR( "Result" ), errorDescription ? "Fail" : "Pass",
23338 CFSTR( "Error Description" ), errorDescription ? errorDescription : "No Error"
23339 );
23340
23341 require_noerr( err, exit );
23342 return;
23343
23344 exit:
23345 ErrQuit( 1, "error: %#m\n", err );
23346 }
23347
23348 //===========================================================================================================================
23349
23350 // Output the final test result to the file (path specified by the user) in the disk.
23351 static void
23352 _DNSSECTestQueryOutputFinalReport( DNSSECTestContext *inContext )
23353 {
23354 OSStatus err;
23355 CFPropertyListRef plist;
23356 NanoTime64 now;
23357 char startTime[ 32 ];
23358 char endTime[ 32 ];
23359
23360 now = NanoTimeGetCurrent();
23361 _NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
23362 _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
23363
23364 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
23365 "{"
23366 "%kO=%s"
23367 "%kO=%s"
23368 "%kO=%s"
23369 "%kO=%b"
23370 "%kO=%O"
23371 "}",
23372 CFSTR( "Start Time" ), startTime,
23373 CFSTR( "End Time" ), endTime,
23374 CFSTR( "Test Case Name" ), inContext->testCaseName,
23375 CFSTR( "All Passed" ), !inContext->subtestFailed,
23376 CFSTR( "Subtest Reports" ), inContext->subtestReport
23377 );
23378 require_noerr( err, exit );
23379 ForgetCF( &inContext->subtestReport );
23380
23381 err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
23382 CFRelease( plist );
23383 require_noerr( err, exit );
23384
23385 return;
23386 exit:
23387 ErrQuit( 1, "error: %#m\n", err );
23388 }
23389
23390 //===========================================================================================================================
23391
23392 // The handler that will be called when timeout happens. When timeout happens it always means something bad happen, and
23393 // it might be possible that all the remaning tests timeout too, so the function exits directly instead of continuing.
23394 static void
23395 _DNSSECTestQueryTimeoutHandler( void *inContext )
23396 {
23397 DNSSECTestContext * const context = (DNSSECTestContext *) inContext;
23398
23399 DNSServiceForget( &context->query );
23400 dispatch_source_forget( &context->queryTimer );
23401
23402 context->subtestFailed = true;
23403 _DNSSECTestQueryWriteSubtestReport( context, "Query for DNSSEC-related records timed out" );
23404 _DNSSECTestQueryOutputFinalReport( context );
23405
23406 exit( 1 );
23407 }
23408
23409 //===========================================================================================================================
23410 #pragma mark - Test case "basic validation"
23411
23412 //===========================================================================================================================
23413
23414 typedef struct DNSSECTest_BasicValidationTestCase
23415 {
23416 const char * queryName; // The query name that controls the behavior of the local DNS server.
23417 uint16_t queryType; // The DNS record being queried.
23418 uint32_t num_of_answers; // How many anwers are expected to be returned.
23419
23420 } DNSSECTest_BasicValidationTestCase;
23421
23422 //===========================================================================================================================
23423
23424 DNSSECTest_BasicValidationTestCase DNSSECTest_BasicValidationTestCases[] =
23425 {
23426 // kDNSSECAlgorithm_RSASHA1 5
23427 { "count-1.z-5-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_A, 1 },
23428 { "count-2.z-5-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_A, 2 },
23429 { "count-1.z-5-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23430 { "count-2.z-5-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23431 { "count-10.z-5-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_A, 10 },
23432 // kDNSSECAlgorithm_RSASHA256 8
23433 { "count-1.z-8-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_A, 1 },
23434 { "count-2.z-8-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_A, 2 },
23435 { "count-1.z-8-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23436 { "count-2.z-8-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23437 { "count-10.z-8-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_A, 10 },
23438 // kDNSSECAlgorithm_RSASHA512 10
23439 { "count-1.z-10-1.z-10-2.z-10-3.dnssec.test.", kDNSServiceType_A, 1 },
23440 { "count-2.z-10-1.z-10-2.z-10-3.dnssec.test.", kDNSServiceType_A, 2 },
23441 { "count-1.z-10-1.z-10-2.z-10-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23442 { "count-2.z-10-1.z-10-2.z-10-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23443 { "count-10.z-10-1.z-10-2.z-10-3.dnssec.test.", kDNSServiceType_A, 10 },
23444 // kDNSSECAlgorithm_ECDSAP256SHA256 13
23445 { "count-1.z-13-1.z-13-2.z-13-3.dnssec.test.", kDNSServiceType_A, 1 },
23446 { "count-2.z-13-1.z-13-2.z-13-3.dnssec.test.", kDNSServiceType_A, 2 },
23447 { "count-1.z-13-1.z-13-2.z-13-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23448 { "count-2.z-13-1.z-13-2.z-13-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23449 { "count-10.z-13-1.z-13-2.z-13-3.dnssec.test.", kDNSServiceType_A, 10 },
23450 // kDNSSECAlgorithm_ECDSAP384SHA384 14
23451 { "count-1.z-14-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_A, 1 },
23452 { "count-2.z-14-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_A, 2 },
23453 { "count-1.z-14-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23454 { "count-2.z-14-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23455 { "count-10.z-14-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_A, 10 },
23456 // Mixed use of mutiple DNSKEY algorithms
23457 { "count-1.z-5-1.z-8-2.z-10-3.dnssec.test.", kDNSServiceType_A, 1 },
23458 { "count-2.z-5-1.z-5-2.z-8-3.dnssec.test.", kDNSServiceType_A, 2 },
23459 { "count-1.z-8-1.z-5-2.z-5-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23460 { "count-2.z-10-1.z-8-2.z-8-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23461 { "count-10.z-10-1.z-8-2.z-5-3.dnssec.test.", kDNSServiceType_A, 10 },
23462 { "count-1.z-13-1.z-14-2.z-14-3.dnssec.test.", kDNSServiceType_A, 1 },
23463 { "count-2.z-14-1.z-13-2.z-8-3.dnssec.test.", kDNSServiceType_A, 2 },
23464 { "count-1.z-5-1.z-14-2.z-8-3.dnssec.test.", kDNSServiceType_AAAA, 1 },
23465 { "count-2.z-10-1.z-8-2.z-13-3.dnssec.test.", kDNSServiceType_AAAA, 2 },
23466 { "count-10.z-14-1.z-13-2.z-5-3.dnssec.test.", kDNSServiceType_A, 10 }
23467 };
23468
23469 //===========================================================================================================================
23470
23471 static void DNSSD_API
23472 _DNSSECTest_BasicValidationCallBack(
23473 DNSServiceRef inSDRef,
23474 DNSServiceFlags inFlags,
23475 uint32_t inInterfaceIndex,
23476 DNSServiceErrorType inError,
23477 const char * inFullName,
23478 uint16_t inType,
23479 uint16_t inClass,
23480 uint16_t inRDataLen,
23481 const void * inRDataPtr,
23482 uint32_t inTTL,
23483 void * inContext );
23484
23485 //===========================================================================================================================
23486
23487 static Boolean
23488 _DNSSECTest_BasicValidationVerifyTheResponse(
23489 DNSSECTestContext * inContext,
23490 DNSServiceFlags inFlags,
23491 uint32_t inInterfaceIndex,
23492 DNSServiceErrorType inError,
23493 const char * inFullName,
23494 uint16_t inType,
23495 uint16_t inClass,
23496 uint16_t inRDataLen,
23497 const void * inRDataPtr,
23498 uint32_t inTTL,
23499 char * inStrBuffer,
23500 uint32_t inBufferLen,
23501 Boolean * outExpectMore );
23502
23503 //===========================================================================================================================
23504
23505 static void DNSSECTest_BasicValidation( DNSSECTestContext *inContext )
23506 {
23507 OSStatus err;
23508 const char * error_description = NULL;
23509
23510 // Get the current subtest
23511 DNSSECTest_BasicValidationTestCase * testCases = &DNSSECTest_BasicValidationTestCases[ inContext->subtestIndex ];
23512 inContext->subtestQueryName = testCases->queryName;
23513 inContext->testCaseContext.basicValidation.number_of_answers = testCases->num_of_answers;
23514
23515 // Issue the query.
23516 err = DNSServiceQueryRecord( &inContext->query,
23517 kDNSServiceFlagsEnableDNSSEC, 0, testCases->queryName, testCases->queryType, kDNSServiceClass_IN,
23518 _DNSSECTest_BasicValidationCallBack, inContext );
23519 require_noerr_action( err, exit, error_description = "DNSServiceQueryRecord failed" );
23520
23521 // Set the dispatch queue.
23522 err = DNSServiceSetDispatchQueue( inContext->query,
23523 dispatch_get_main_queue() );
23524 require_noerr_action( err, exit, error_description = "DNSServiceSetDispatchQueue failed" );
23525
23526 // Create a timer to handle timeout.
23527 err = DispatchTimerCreate( dispatch_time_seconds( kDNSSECTestQueryTimeoutSecs ), DISPATCH_TIME_FOREVER,
23528 UINT64_C_safe( kDNSSECTestQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL, _DNSSECTestQueryTimeoutHandler,
23529 NULL, inContext, &inContext->queryTimer );
23530 require_noerr_action( err, exit, error_description = "DispatchTimerCreate failed" );
23531 dispatch_resume( inContext->queryTimer );
23532
23533 // start the current subtest, and record the start time.
23534 inContext->subtestStartTime = NanoTimeGetCurrent();
23535 if( !inContext->testStarted )
23536 {
23537 // Only call dispatch_main once, when we first start the test.
23538 inContext->testStarted = true;
23539 dispatch_main();
23540 }
23541
23542 return;
23543 exit:
23544 _DNSSECTestQueryWriteSubtestReport( inContext, error_description );
23545 _DNSSECTestQueryOutputFinalReport( inContext );
23546 exit( 1 );
23547 }
23548
23549 //===========================================================================================================================
23550
23551 static void DNSSD_API
23552 _DNSSECTest_BasicValidationCallBack(
23553 DNSServiceRef inSDRef,
23554 DNSServiceFlags inFlags,
23555 uint32_t inInterfaceIndex,
23556 DNSServiceErrorType inError,
23557 const char * inFullName,
23558 uint16_t inType,
23559 uint16_t inClass,
23560 uint16_t inRDataLen,
23561 const void * inRDataPtr,
23562 uint32_t inTTL,
23563 void * inContext )
23564 {
23565 char err_desp_str[1024];
23566 Boolean valid;
23567 Boolean expectMore;
23568 DNSSECTestContext * const context = (DNSSECTestContext *)inContext;
23569
23570 Unused( inSDRef );
23571
23572 err_desp_str[0] = '\0';
23573
23574 // Verify if the response is expected
23575 valid = _DNSSECTest_BasicValidationVerifyTheResponse(context, inFlags, inInterfaceIndex, inError, inFullName, inType,
23576 inClass, inRDataLen, inRDataPtr, inTTL, err_desp_str, sizeof( err_desp_str ), &expectMore);
23577
23578 // If more answers are expected to be returned.
23579 if( expectMore )
23580 {
23581 return;
23582 }
23583
23584 // Cancel the current request and its corresponding timer.
23585 DNSServiceForget( &context->query );
23586 dispatch_source_forget( &context->queryTimer );
23587
23588 // Check if the test fails.
23589 if( !valid )
23590 {
23591 context->subtestFailed = true;
23592 }
23593 _DNSSECTestQueryWriteSubtestReport( context, valid ? NULL : err_desp_str );
23594
23595 // Increment the index.
23596 context->subtestIndex++;
23597 if( context->subtestIndex == countof( DNSSECTest_BasicValidationTestCases ) )
23598 {
23599 // All test cases have been run
23600 _DNSSECTestQueryOutputFinalReport( context );
23601 exit( context->subtestFailed ? 2 : 0 );
23602 }
23603
23604 // Start the next subtest.
23605 DNSSECTest_BasicValidation( context );
23606 }
23607
23608 //===========================================================================================================================
23609
23610 // Check if the response is expected. Return true if the response is expected, otherwise return false.
23611 static Boolean
23612 _DNSSECTest_BasicValidationVerifyTheResponse(
23613 DNSSECTestContext * inContext,
23614 DNSServiceFlags inFlags,
23615 uint32_t inInterfaceIndex,
23616 DNSServiceErrorType inError,
23617 const char * inFullName,
23618 uint16_t inType,
23619 uint16_t inClass,
23620 uint16_t inRDataLen,
23621 const void * inRDataPtr,
23622 uint32_t inTTL,
23623 char * inStrBuffer,
23624 uint32_t inBufferLen,
23625 Boolean * outExpectMore )
23626 {
23627 const char * const queryName = inContext->subtestQueryName;
23628 DNSSECTest_BasicValidationTestCase * testCases = &DNSSECTest_BasicValidationTestCases[ inContext->subtestIndex ];
23629 DNSSECTest_BasicValidationContext * subContext = &inContext->testCaseContext.basicValidation;
23630 Boolean expectMore = false;
23631
23632 Unused( inClass );
23633 Unused( inRDataLen );
23634 Unused( inRDataPtr );
23635 Unused( inTTL );
23636
23637 require_noerr_action( inError, exit, SNPrintF( inStrBuffer, inBufferLen, "Unexpected DNSServiceErrorType: %d", inError ) );
23638
23639 require_action( inFlags & kDNSServiceFlagsAdd, exit,
23640 SNPrintF( inStrBuffer, inBufferLen, "Unexpected add/remove event: %#{flags}", inFlags, kDNSServiceFlagsDescriptors )
23641 );
23642
23643 require_action( inInterfaceIndex == kDNSServiceInterfaceIndexAny, exit,
23644 SNPrintF( inStrBuffer, inBufferLen, "Non-local-only interface is used: %u", inInterfaceIndex ) );
23645
23646 require_action( strcmp( queryName, inFullName ) == 0, exit,
23647 SNPrintF( inStrBuffer, inBufferLen, "Mismatched name: %s", inFullName ) );
23648
23649 require_action( inType == testCases->queryType, exit,
23650 SNPrintF( inStrBuffer, inBufferLen, "Mismatched query type: %s", RecordTypeToString( inType ) ) );
23651
23652 require_action( (inFlags & kDNSServiceFlagsSecure) == kDNSServiceFlagsSecure, exit,
23653 SNPrintF( inStrBuffer, inBufferLen, "Unexpected DNSSEC result: %#{flags}", (inFlags & kDNSServiceFlagsSecure),
23654 kDNSServiceFlagsDescriptors )
23655 );
23656
23657 if( --subContext->number_of_answers != 0 )
23658 {
23659 expectMore = true;
23660 }
23661
23662
23663 exit:
23664 if( outExpectMore )
23665 {
23666 *outExpectMore = expectMore;
23667 }
23668
23669 return inStrBuffer[0] != '\0' ? false : true;
23670 }
23671 #else
23672 static void DNSSECTestCmd( void )
23673 {
23674 exit( 0 );
23675 }
23676 #endif // #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
23677
23678 //===========================================================================================================================
23679 // SSDPDiscoverCmd
23680 //===========================================================================================================================
23681
23682 #define kSSDPPort 1900
23683
23684 typedef struct
23685 {
23686 HTTPHeader header; // HTTP header object for sending and receiving.
23687 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
23688 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
23689 int receiveSecs; // After send, the amount of time to spend receiving.
23690 uint32_t ifindex; // Index of the interface over which to send the query.
23691 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
23692 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
23693
23694 } SSDPDiscoverContext;
23695
23696 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
23697 static void SSDPDiscoverReadHandler( void *inContext );
23698 static int SocketToPortNumber( SocketRef inSock );
23699 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
23700
23701 static void SSDPDiscoverCmd( void )
23702 {
23703 OSStatus err;
23704 struct timeval now;
23705 SSDPDiscoverContext * context;
23706 dispatch_source_t signalSource = NULL;
23707 SocketRef sockV4 = kInvalidSocketRef;
23708 SocketRef sockV6 = kInvalidSocketRef;
23709 ssize_t n;
23710 int sendCount;
23711
23712 // Set up SIGINT handler.
23713
23714 signal( SIGINT, SIG_IGN );
23715 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
23716 require_noerr( err, exit );
23717 dispatch_resume( signalSource );
23718
23719 // Check command parameters.
23720
23721 if( gSSDPDiscover_ReceiveSecs < -1 )
23722 {
23723 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
23724 err = kParamErr;
23725 goto exit;
23726 }
23727
23728 // Create context.
23729
23730 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
23731 require_action( context, exit, err = kNoMemoryErr );
23732
23733 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
23734 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
23735 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
23736
23737 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
23738 require_noerr_quiet( err, exit );
23739
23740 // Set up IPv4 socket.
23741
23742 if( context->useIPv4 )
23743 {
23744 int port;
23745 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
23746 require_noerr( err, exit );
23747
23748 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
23749 require_noerr( err, exit );
23750
23751 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
23752 err = map_socket_noerr_errno( sockV4, err );
23753 require_noerr( err, exit );
23754 }
23755
23756 // Set up IPv6 socket.
23757
23758 if( context->useIPv6 )
23759 {
23760 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
23761 require_noerr( err, exit );
23762
23763 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
23764 require_noerr( err, exit );
23765
23766 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
23767 err = map_socket_noerr_errno( sockV6, err );
23768 require_noerr( err, exit );
23769 }
23770
23771 // Print prologue.
23772
23773 SSDPDiscoverPrintPrologue( context );
23774
23775 // Send mDNS query message.
23776
23777 sendCount = 0;
23778 if( IsValidSocket( sockV4 ) )
23779 {
23780 struct sockaddr_in mcastAddr4;
23781
23782 _SockAddrInitIPv4( &mcastAddr4, UINT32_C( 0xEFFFFFFA ), kSSDPPort ); // 239.255.255.250
23783
23784 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
23785 require_noerr( err, exit );
23786
23787 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
23788 (socklen_t) sizeof( mcastAddr4 ) );
23789 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
23790 if( err )
23791 {
23792 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
23793 ForgetSocket( &sockV4 );
23794 }
23795 else
23796 {
23797 if( gSSDPDiscover_Verbose )
23798 {
23799 gettimeofday( &now, NULL );
23800 FPrintF( stdout, "---\n" );
23801 FPrintF( stdout, "Send time: %{du:time}\n", &now );
23802 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
23803 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
23804 FPrintF( stdout, "Message size: %zu\n", context->header.len );
23805 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
23806 }
23807 ++sendCount;
23808 }
23809 }
23810
23811 if( IsValidSocket( sockV6 ) )
23812 {
23813 struct sockaddr_in6 mcastAddr6;
23814
23815 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
23816 SIN6_LEN_SET( &mcastAddr6 );
23817 mcastAddr6.sin6_family = AF_INET6;
23818 mcastAddr6.sin6_port = htons( kSSDPPort );
23819 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
23820 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
23821 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
23822
23823 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
23824 require_noerr( err, exit );
23825
23826 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
23827 (socklen_t) sizeof( mcastAddr6 ) );
23828 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
23829 if( err )
23830 {
23831 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
23832 ForgetSocket( &sockV6 );
23833 }
23834 else
23835 {
23836 if( gSSDPDiscover_Verbose )
23837 {
23838 gettimeofday( &now, NULL );
23839 FPrintF( stdout, "---\n" );
23840 FPrintF( stdout, "Send time: %{du:time}\n", &now );
23841 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
23842 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
23843 FPrintF( stdout, "Message size: %zu\n", context->header.len );
23844 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
23845 }
23846 ++sendCount;
23847 }
23848 }
23849 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
23850
23851 // If there's no wait period after the send, then exit.
23852
23853 if( context->receiveSecs == 0 ) goto exit;
23854
23855 // Create dispatch read sources for socket(s).
23856
23857 if( IsValidSocket( sockV4 ) )
23858 {
23859 SocketContext * sockCtx;
23860
23861 sockCtx = SocketContextCreate( sockV4, context, &err );
23862 require_noerr( err, exit );
23863 sockV4 = kInvalidSocketRef;
23864
23865 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
23866 &context->readSourceV4 );
23867 if( err ) ForgetSocketContext( &sockCtx );
23868 require_noerr( err, exit );
23869
23870 dispatch_resume( context->readSourceV4 );
23871 }
23872
23873 if( IsValidSocket( sockV6 ) )
23874 {
23875 SocketContext * sockCtx;
23876
23877 sockCtx = SocketContextCreate( sockV6, context, &err );
23878 require_noerr( err, exit );
23879 sockV6 = kInvalidSocketRef;
23880
23881 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
23882 &context->readSourceV6 );
23883 if( err ) ForgetSocketContext( &sockCtx );
23884 require_noerr( err, exit );
23885
23886 dispatch_resume( context->readSourceV6 );
23887 }
23888
23889 if( context->receiveSecs > 0 )
23890 {
23891 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
23892 Exit );
23893 }
23894 dispatch_main();
23895
23896 exit:
23897 ForgetSocket( &sockV4 );
23898 ForgetSocket( &sockV6 );
23899 dispatch_source_forget( &signalSource );
23900 exit( err ? 1 : 0 );
23901 }
23902
23903 static int SocketToPortNumber( SocketRef inSock )
23904 {
23905 OSStatus err;
23906 sockaddr_ip sip;
23907 socklen_t len;
23908
23909 len = (socklen_t) sizeof( sip );
23910 err = getsockname( inSock, &sip.sa, &len );
23911 err = map_socket_noerr_errno( inSock, err );
23912 check_noerr( err );
23913 return( err ? -1 : SockAddrGetPort( &sip ) );
23914 }
23915
23916 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
23917 {
23918 OSStatus err;
23919
23920 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
23921 require_noerr( err, exit );
23922
23923 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
23924 require_noerr( err, exit );
23925
23926 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
23927 require_noerr( err, exit );
23928
23929 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
23930 require_noerr( err, exit );
23931
23932 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
23933 require_noerr( err, exit );
23934
23935 err = HTTPHeader_Commit( inHeader );
23936 require_noerr( err, exit );
23937
23938 exit:
23939 return( err );
23940 }
23941
23942 //===========================================================================================================================
23943 // SSDPDiscoverPrintPrologue
23944 //===========================================================================================================================
23945
23946 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
23947 {
23948 const int receiveSecs = inContext->receiveSecs;
23949 const char * ifName;
23950 char ifNameBuf[ IF_NAMESIZE + 1 ];
23951 NetTransportType ifType;
23952
23953 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
23954
23955 ifType = kNetTransportType_Undefined;
23956 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
23957
23958 FPrintF( stdout, "Interface: %s/%d/%s\n",
23959 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
23960 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
23961 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
23962 FPrintF( stdout, "Receive duration: " );
23963 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
23964 else FPrintF( stdout, "∞\n" );
23965 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
23966 }
23967
23968 //===========================================================================================================================
23969 // SSDPDiscoverReadHandler
23970 //===========================================================================================================================
23971
23972 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader );
23973
23974 static void SSDPDiscoverReadHandler( void *inContext )
23975 {
23976 OSStatus err;
23977 struct timeval now;
23978 SocketContext * const sockCtx = (SocketContext *) inContext;
23979 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
23980 HTTPHeader * const header = &context->header;
23981 sockaddr_ip fromAddr;
23982 size_t msgLen;
23983
23984 gettimeofday( &now, NULL );
23985
23986 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
23987 NULL, NULL, NULL, NULL );
23988 require_noerr( err, exit );
23989
23990 FPrintF( stdout, "---\n" );
23991 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
23992 FPrintF( stdout, "Source: %##a\n", &fromAddr );
23993 FPrintF( stdout, "Message size: %zu\n", msgLen );
23994 header->len = msgLen;
23995 if( _HTTPHeader_Validate( header ) )
23996 {
23997 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
23998 if( header->extraDataLen > 0 )
23999 {
24000 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
24001 }
24002 }
24003 else
24004 {
24005 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
24006 goto exit;
24007 }
24008
24009 exit:
24010 if( err ) exit( 1 );
24011 }
24012
24013 //===========================================================================================================================
24014 // _HTTPHeader_Validate
24015 //
24016 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
24017 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
24018 //
24019 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
24020 //===========================================================================================================================
24021
24022 static Boolean _HTTPHeader_Validate( HTTPHeader *inHeader )
24023 {
24024 const char * src;
24025 const char * end;
24026
24027 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
24028
24029 require( inHeader->len < sizeof( inHeader->buf ), exit );
24030 src = inHeader->buf;
24031 end = src + inHeader->len;
24032 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
24033 {
24034 src += 4;
24035 }
24036 else
24037 {
24038 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
24039 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
24040
24041 for( ;; )
24042 {
24043 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
24044 if( src >= end ) goto exit;
24045 ++src;
24046 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
24047 {
24048 src += 2;
24049 break;
24050 }
24051 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
24052 {
24053 src += 1;
24054 break;
24055 }
24056 }
24057 }
24058 inHeader->extraDataPtr = src;
24059 inHeader->extraDataLen = (size_t)( end - src );
24060 inHeader->len = (size_t)( src - inHeader->buf );
24061 return( true );
24062
24063 exit:
24064 return( false );
24065 }
24066
24067 #if( TARGET_OS_DARWIN )
24068 //===========================================================================================================================
24069 // ResQueryCmd
24070 //===========================================================================================================================
24071
24072 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
24073
24074 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
24075 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
24076 int,
24077 ( const char *dname, int class, int type, u_char *answer, int anslen ),
24078 ( dname, class, type, answer, anslen ) );
24079
24080 // res_query() from libinfo
24081
24082 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
24083 SOFT_LINK_FUNCTION_EX( info, res_query,
24084 int,
24085 ( const char *dname, int class, int type, u_char *answer, int anslen ),
24086 ( dname, class, type, answer, anslen ) );
24087
24088 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
24089
24090 static void ResQueryCmd( void )
24091 {
24092 OSStatus err;
24093 res_query_f res_query_ptr;
24094 int n;
24095 uint16_t type, class;
24096 uint8_t answer[ 1024 ];
24097
24098 // Get pointer to one of the res_query() functions.
24099
24100 if( gResQuery_UseLibInfo )
24101 {
24102 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
24103 {
24104 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
24105 err = kNotFoundErr;
24106 goto exit;
24107 }
24108 res_query_ptr = soft_res_query;
24109 }
24110 else
24111 {
24112 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
24113 {
24114 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
24115 err = kNotFoundErr;
24116 goto exit;
24117 }
24118 res_query_ptr = soft_res_9_query;
24119 }
24120
24121 // Get record type.
24122
24123 err = RecordTypeFromArgString( gResQuery_Type, &type );
24124 require_noerr( err, exit );
24125
24126 // Get record class.
24127
24128 if( gResQuery_Class )
24129 {
24130 err = RecordClassFromArgString( gResQuery_Class, &class );
24131 require_noerr( err, exit );
24132 }
24133 else
24134 {
24135 class = kDNSServiceClass_IN;
24136 }
24137
24138 // Print prologue.
24139
24140 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
24141 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
24142 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
24143 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
24144 FPrintF( stdout, "---\n" );
24145
24146 // Call res_query().
24147
24148 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
24149 if( n < 0 )
24150 {
24151 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
24152 err = kUnknownErr;
24153 goto exit;
24154 }
24155
24156 // Print result.
24157
24158 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}\n", n, answer, (size_t) n );
24159
24160 exit:
24161 if( err ) exit( 1 );
24162 }
24163
24164 //===========================================================================================================================
24165 // ResolvDNSQueryCmd
24166 //===========================================================================================================================
24167
24168 // 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
24169 // avoid including the header file.
24170
24171 typedef void * dns_handle_t;
24172
24173 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
24174 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
24175 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
24176 int32_t, (
24177 dns_handle_t dns,
24178 const char * name,
24179 uint32_t dnsclass,
24180 uint32_t dnstype,
24181 char * buf,
24182 uint32_t len,
24183 struct sockaddr * from,
24184 uint32_t * fromlen ),
24185 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
24186
24187 static void ResolvDNSQueryCmd( void )
24188 {
24189 OSStatus err;
24190 int n;
24191 dns_handle_t dns = NULL;
24192 uint16_t type, class;
24193 sockaddr_ip from;
24194 uint32_t fromLen;
24195 uint8_t answer[ 1024 ];
24196
24197 // Make sure that the required symbols are available.
24198
24199 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
24200 {
24201 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
24202 err = kNotFoundErr;
24203 goto exit;
24204 }
24205
24206 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
24207 {
24208 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
24209 err = kNotFoundErr;
24210 goto exit;
24211 }
24212
24213 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
24214 {
24215 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
24216 err = kNotFoundErr;
24217 goto exit;
24218 }
24219
24220 // Get record type.
24221
24222 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
24223 require_noerr( err, exit );
24224
24225 // Get record class.
24226
24227 if( gResolvDNSQuery_Class )
24228 {
24229 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
24230 require_noerr( err, exit );
24231 }
24232 else
24233 {
24234 class = kDNSServiceClass_IN;
24235 }
24236
24237 // Get dns handle.
24238
24239 dns = soft_dns_open( gResolvDNSQuery_Path );
24240 if( !dns )
24241 {
24242 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
24243 err = kUnknownErr;
24244 goto exit;
24245 }
24246
24247 // Print prologue.
24248
24249 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
24250 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
24251 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
24252 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
24253 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
24254 FPrintF( stdout, "---\n" );
24255
24256 // Call dns_query().
24257
24258 memset( &from, 0, sizeof( from ) );
24259 fromLen = (uint32_t) sizeof( from );
24260 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
24261 &fromLen );
24262 if( n < 0 )
24263 {
24264 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
24265 err = kUnknownErr;
24266 goto exit;
24267 }
24268
24269 // Print result.
24270
24271 FPrintF( stdout, "From: %##a\n", &from );
24272 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}\n", n, answer, (size_t) n );
24273
24274 exit:
24275 if( dns ) soft_dns_free( dns );
24276 if( err ) exit( 1 );
24277 }
24278
24279 //===========================================================================================================================
24280 // CFHostCmd
24281 //===========================================================================================================================
24282
24283 static void
24284 _CFHostResolveCallback(
24285 CFHostRef inHost,
24286 CFHostInfoType inInfoType,
24287 const CFStreamError * inError,
24288 void * inInfo );
24289
24290 static void CFHostCmd( void )
24291 {
24292 OSStatus err;
24293 CFStringRef name;
24294 Boolean success;
24295 CFHostRef host = NULL;
24296 CFHostClientContext context;
24297 CFStreamError streamErr;
24298
24299 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
24300 require_action( name, exit, err = kUnknownErr );
24301
24302 host = CFHostCreateWithName( kCFAllocatorDefault, name );
24303 ForgetCF( &name );
24304 require_action( host, exit, err = kUnknownErr );
24305
24306 memset( &context, 0, sizeof( context ) );
24307 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
24308 require_action( success, exit, err = kUnknownErr );
24309
24310 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
24311
24312 // Print prologue.
24313
24314 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
24315 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
24316 FPrintF( stdout, "---\n" );
24317
24318 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
24319 require_action( success, exit, err = kUnknownErr );
24320 err = kNoErr;
24321
24322 CFRunLoopRun();
24323
24324 exit:
24325 CFReleaseNullSafe( host );
24326 if( err ) exit( 1 );
24327 }
24328
24329 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
24330 {
24331 OSStatus err;
24332 struct timeval now;
24333
24334 gettimeofday( &now, NULL );
24335
24336 Unused( inInfoType );
24337 Unused( inInfo );
24338
24339 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
24340 {
24341 err = inError->error;
24342 if( inError->domain == kCFStreamErrorDomainNetDB )
24343 {
24344 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
24345 }
24346 else
24347 {
24348 FPrintF( stderr, "Error %#m\n", err );
24349 }
24350 }
24351 else
24352 {
24353 CFArrayRef addresses;
24354 CFIndex count, i;
24355 CFDataRef addrData;
24356 const struct sockaddr * sockAddr;
24357 Boolean wasResolved = false;
24358
24359 addresses = CFHostGetAddressing( inHost, &wasResolved );
24360 check( wasResolved );
24361
24362 if( addresses )
24363 {
24364 count = CFArrayGetCount( addresses );
24365 for( i = 0; i < count; ++i )
24366 {
24367 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
24368 require_noerr( err, exit );
24369
24370 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
24371 FPrintF( stdout, "%##a\n", sockAddr );
24372 }
24373 }
24374 err = kNoErr;
24375 }
24376
24377 FPrintF( stdout, "---\n" );
24378 FPrintF( stdout, "End time: %{du:time}\n", &now );
24379
24380 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
24381
24382 exit:
24383 exit( err ? 1 : 0 );
24384 }
24385
24386 //===========================================================================================================================
24387 // DNSConfigAddCmd
24388 //
24389 // Note: Based on ajn's supplemental test tool.
24390 //===========================================================================================================================
24391
24392 static void DNSConfigAddCmd( void )
24393 {
24394 OSStatus err;
24395 CFMutableDictionaryRef dict = NULL;
24396 CFMutableArrayRef array = NULL;
24397 size_t i;
24398 SCDynamicStoreRef store = NULL;
24399 CFStringRef key = NULL;
24400 Boolean success;
24401
24402 // Create dictionary.
24403
24404 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
24405 require_action( dict, exit, err = kNoMemoryErr );
24406
24407 // Add DNS server IP addresses.
24408
24409 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
24410 require_action( array, exit, err = kNoMemoryErr );
24411
24412 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
24413 {
24414 CFStringRef addrStr;
24415
24416 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
24417 require_action( addrStr, exit, err = kUnknownErr );
24418
24419 CFArrayAppendValue( array, addrStr );
24420 CFRelease( addrStr );
24421 }
24422
24423 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
24424 ForgetCF( &array );
24425
24426 // Add domains, if any.
24427
24428 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
24429 require_action( array, exit, err = kNoMemoryErr );
24430
24431 if( gDNSConfigAdd_DomainCount > 0 )
24432 {
24433 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
24434 {
24435 CFStringRef domainStr;
24436
24437 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
24438 require_action( domainStr, exit, err = kUnknownErr );
24439
24440 CFArrayAppendValue( array, domainStr );
24441 CFRelease( domainStr );
24442 }
24443 }
24444 else
24445 {
24446 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
24447
24448 CFArrayAppendValue( array, CFSTR( "" ) );
24449 }
24450 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
24451 ForgetCF( &array );
24452
24453 // Set search orders.
24454
24455 if( gDNSConfigAdd_SearchOrder >= 0 )
24456 {
24457 const size_t n = Min( gDNSConfigAdd_DomainCount, 1 );
24458
24459 array = CFArrayCreateMutable( NULL, (CFIndex) n, &kCFTypeArrayCallBacks );
24460 require_action( array, exit, err = kNoMemoryErr );
24461
24462 for( i = 0; i < n; ++i )
24463 {
24464 err = CFArrayAppendInt64( array, gDNSConfigAdd_SearchOrder );
24465 require_noerr( err, exit );
24466 }
24467 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchOrders, array );
24468 ForgetCF( &array );
24469 }
24470
24471 // Add interface, if any.
24472
24473 if( gDNSConfigAdd_Interface )
24474 {
24475 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
24476 require_noerr( err, exit );
24477
24478 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
24479 }
24480
24481 // Set dictionary in dynamic store.
24482
24483 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
24484 err = map_scerror( store );
24485 require_noerr( err, exit );
24486
24487 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
24488 require_action( key, exit, err = kUnknownErr );
24489
24490 success = SCDynamicStoreSetValue( store, key, dict );
24491 require_action( success, exit, err = kUnknownErr );
24492
24493 exit:
24494 CFReleaseNullSafe( dict );
24495 CFReleaseNullSafe( array );
24496 CFReleaseNullSafe( store );
24497 CFReleaseNullSafe( key );
24498 gExitCode = err ? 1 : 0;
24499 }
24500
24501 //===========================================================================================================================
24502 // DNSConfigRemoveCmd
24503 //===========================================================================================================================
24504
24505 static void DNSConfigRemoveCmd( void )
24506 {
24507 OSStatus err;
24508 SCDynamicStoreRef store = NULL;
24509 CFStringRef key = NULL;
24510 Boolean success;
24511
24512 store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
24513 err = map_scerror( store );
24514 require_noerr( err, exit );
24515
24516 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
24517 require_action( key, exit, err = kUnknownErr );
24518
24519 success = SCDynamicStoreRemoveValue( store, key );
24520 require_action( success, exit, err = kUnknownErr );
24521
24522 exit:
24523 CFReleaseNullSafe( store );
24524 CFReleaseNullSafe( key );
24525 gExitCode = err ? 1 : 0;
24526 }
24527
24528 //===========================================================================================================================
24529 // XPCSendCmd
24530 //===========================================================================================================================
24531
24532 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
24533
24534 static void XPCSendCmd( void )
24535 {
24536 OSStatus err;
24537 xpc_object_t msg, reply;
24538
24539 err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
24540 require_noerr_quiet( err, exit );
24541
24542 FPrintF( stdout, "Service: %s\n", gXPCSend_ServiceName );
24543 FPrintF( stdout, "Message: %s\n", gXPCSend_MessageStr );
24544 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
24545 FPrintF( stdout, "---\n" );
24546 FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
24547
24548 err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
24549 xpc_forget( &msg );
24550 require_noerr_quiet( err, exit );
24551
24552 FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
24553 FPrintF( stdout, "---\n" );
24554 FPrintF( stdout, "End time: %{du:time}\n", NULL );
24555 xpc_forget( &reply );
24556
24557 exit:
24558 if( err ) ErrQuit( 1, "error: %#m\n", err );
24559 }
24560
24561 //===========================================================================================================================
24562 // _XPCDictionaryCreateFromString
24563 //===========================================================================================================================
24564
24565 #define kXPCObjectPrefix_Bool "bool:"
24566 #define kXPCObjectPrefix_Data "data:"
24567 #define kXPCObjectPrefix_Int64 "int:"
24568 #define kXPCObjectPrefix_String "string:"
24569 #define kXPCObjectPrefix_UInt64 "uint:"
24570 #define kXPCObjectPrefix_UUID "uuid:"
24571
24572 typedef struct XPCListItem XPCListItem;
24573 struct XPCListItem
24574 {
24575 XPCListItem * next;
24576 xpc_object_t obj;
24577 char * key;
24578 };
24579
24580 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
24581 static void _XPCListItemFree( XPCListItem *inItem );
24582 static void _XPCListFree( XPCListItem *inList );
24583
24584 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject );
24585
24586 static OSStatus _XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
24587 {
24588 OSStatus err;
24589 xpc_object_t container;
24590 const char * ptr = inString;
24591 const char * const end = inString + strlen( inString );
24592 XPCListItem * list = NULL;
24593
24594 container = xpc_dictionary_create( NULL, NULL, 0 );
24595 require_action( container, exit, err = kNoMemoryErr );
24596
24597 while( *ptr )
24598 {
24599 xpc_type_t containerType;
24600 xpc_object_t value;
24601 int c;
24602 char keyStr[ 256 ];
24603 char valStr[ 256 ];
24604
24605 // At this point, zero or more of the current container's elements have been parsed.
24606 // Skip the white space leading up to the container's next element, if any, or the container's end.
24607
24608 while( isspace_safe( *ptr ) ) ++ptr;
24609
24610 // Check if we're done with the current container.
24611
24612 c = *ptr;
24613 if( c == '\0' ) break;
24614
24615 containerType = xpc_get_type( container );
24616 if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
24617 ( ( containerType == XPC_TYPE_ARRAY ) && ( c == ']' ) ) )
24618 {
24619 XPCListItem * item;
24620
24621 item = list;
24622 require_action_quiet( item, exit, err = kMalformedErr );
24623
24624 ++ptr;
24625
24626 // Add the current container to its parent container.
24627
24628 if( item->key )
24629 {
24630 xpc_dictionary_set_value( item->obj, item->key, container );
24631 }
24632 else
24633 {
24634 xpc_array_append_value( item->obj, container );
24635 }
24636
24637 // Continue with the parent container.
24638
24639 xpc_release( container );
24640 container = xpc_retain( item->obj );
24641 list = item->next;
24642 _XPCListItemFree( item );
24643 continue;
24644 }
24645
24646 // If the current container is a dictionary, parse the key string.
24647
24648 if( containerType == XPC_TYPE_DICTIONARY )
24649 {
24650 err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
24651 require_noerr_quiet( err, exit );
24652
24653 c = *ptr;
24654 require_action_quiet( c == '=', exit, err = kMalformedErr );
24655 ++ptr;
24656 }
24657
24658 // Check if the value is a dictionary ({...}) or an array ([...]).
24659
24660 c = *ptr;
24661 if( ( c == '{' ) || ( c == '[' ) )
24662 {
24663 XPCListItem * item;
24664
24665 ++ptr;
24666
24667 // Save the current container.
24668
24669 err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
24670 require_noerr( err, exit );
24671
24672 item->next = list;
24673 list = item;
24674 item = NULL;
24675
24676 // Create and continue with the child container.
24677
24678 xpc_release( container );
24679 if( c == '{' )
24680 {
24681 container = xpc_dictionary_create( NULL, NULL, 0 );
24682 require_action( container, exit, err = kNoMemoryErr );
24683 }
24684 else
24685 {
24686 container = xpc_array_create( NULL, 0 );
24687 require_action( container, exit, err = kNoMemoryErr );
24688 }
24689 continue;
24690 }
24691
24692 // Parse the value string.
24693
24694 err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
24695 require_noerr_quiet( err, exit );
24696
24697 err = _XPCObjectFromString( valStr, &value );
24698 require_noerr_quiet( err, exit );
24699
24700 if( containerType == XPC_TYPE_DICTIONARY )
24701 {
24702 xpc_dictionary_set_value( container, keyStr, value );
24703 }
24704 else
24705 {
24706 xpc_array_append_value( container, value );
24707 }
24708 xpc_forget( &value );
24709 }
24710 require_action_quiet( !list, exit, err = kMalformedErr );
24711
24712 check( container );
24713 check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
24714
24715 *outDict = container;
24716 container = NULL;
24717 err = kNoErr;
24718
24719 exit:
24720 xpc_release_null_safe( container );
24721 if( list ) _XPCListFree( list );
24722 return( err );
24723 }
24724
24725 static OSStatus _XPCObjectFromString( const char *inString, xpc_object_t *outObject )
24726 {
24727 OSStatus err;
24728 xpc_object_t object;
24729
24730 if( 0 ) {}
24731
24732 // Bool
24733
24734 else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
24735 {
24736 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Bool );
24737 bool value;
24738
24739 if( IsTrueString( str, kSizeCString ) )
24740 {
24741 value = true;
24742 }
24743 else if( IsFalseString( str, kSizeCString ) )
24744 {
24745 value = false;
24746 }
24747 else
24748 {
24749 err = kValueErr;
24750 goto exit;
24751 }
24752
24753 object = xpc_bool_create( value );
24754 require_action( object, exit, err = kNoMemoryErr );
24755 }
24756
24757 // Data
24758
24759 else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
24760 {
24761 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Data );
24762 uint8_t * dataPtr;
24763 size_t dataLen;
24764
24765 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
24766 require_noerr( err, exit );
24767
24768 object = xpc_data_create( dataPtr, dataLen );
24769 free( dataPtr );
24770 require_action( object, exit, err = kNoMemoryErr );
24771 }
24772
24773 // Int64
24774
24775 else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
24776 {
24777 const char * const str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
24778 int64_t i64;
24779
24780 i64 = _StringToInt64( str, &err );
24781 require_noerr_quiet( err, exit );
24782
24783 object = xpc_int64_create( i64 );
24784 require_action( object, exit, err = kNoMemoryErr );
24785 }
24786
24787 // String
24788
24789 else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
24790 {
24791 const char * const str = inString + sizeof_string( kXPCObjectPrefix_String );
24792
24793 object = xpc_string_create( str );
24794 require_action( object, exit, err = kNoMemoryErr );
24795 }
24796
24797 // UInt64
24798
24799 else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
24800 {
24801 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
24802 uint64_t u64;
24803
24804 u64 = _StringToUInt64( str, &err );
24805 require_noerr_quiet( err, exit );
24806
24807 object = xpc_uint64_create( u64 );
24808 require_action( object, exit, err = kNoMemoryErr );
24809 }
24810
24811 // UUID
24812
24813 else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
24814 {
24815 const char * const str = inString + sizeof_string( kXPCObjectPrefix_UUID );
24816 uuid_t uuid;
24817
24818 err = uuid_parse( str, uuid );
24819 require_noerr_action_quiet( err, exit, err = kValueErr );
24820
24821 object = xpc_uuid_create( uuid );
24822 require_action( object, exit, err = kNoMemoryErr );
24823 }
24824
24825 // Unsupported prefix
24826
24827 else
24828 {
24829 err = kValueErr;
24830 goto exit;
24831 }
24832
24833 *outObject = object;
24834 err = kNoErr;
24835
24836 exit:
24837 return( err );
24838 }
24839
24840 static OSStatus _XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
24841 {
24842 OSStatus err;
24843 XPCListItem * item;
24844
24845 item = (XPCListItem *) calloc( 1, sizeof( *item ) );
24846 require_action( item, exit, err = kNoMemoryErr );
24847
24848 item->obj = xpc_retain( inObject );
24849 if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
24850 {
24851 item->key = strdup( inKey );
24852 require_action( item->key, exit, err = kNoMemoryErr );
24853 }
24854
24855 *outItem = item;
24856 item = NULL;
24857 err = kNoErr;
24858
24859 exit:
24860 if( item ) _XPCListItemFree( item );
24861 return( err );
24862 }
24863
24864 static void _XPCListItemFree( XPCListItem *inItem )
24865 {
24866 xpc_forget( &inItem->obj );
24867 ForgetMem( &inItem->key );
24868 free( inItem );
24869 }
24870
24871 static void _XPCListFree( XPCListItem *inList )
24872 {
24873 XPCListItem * item;
24874
24875 while( ( item = inList ) != NULL )
24876 {
24877 inList = item->next;
24878 _XPCListItemFree( item );
24879 }
24880 }
24881 #endif // TARGET_OS_DARWIN
24882
24883 #if( MDNSRESPONDER_PROJECT )
24884 //===========================================================================================================================
24885 // InterfaceMonitorCmd
24886 //===========================================================================================================================
24887
24888 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
24889 static void _InterfaceMonitorSignalHandler( void *inContext );
24890
24891 static void InterfaceMonitorCmd( void )
24892 {
24893 OSStatus err;
24894 mdns_interface_monitor_t monitor;
24895 dispatch_source_t signalSource = NULL;
24896 uint32_t ifIndex;
24897 __block int exitCode;
24898
24899 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
24900 require_noerr_quiet( err, exit );
24901
24902 monitor = mdns_interface_monitor_create( ifIndex );
24903 require_action( monitor, exit, err = kNoResourcesErr );
24904
24905 exitCode = 0;
24906 mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
24907 mdns_interface_monitor_set_event_handler( monitor,
24908 ^( mdns_event_t inEvent, OSStatus inError )
24909 {
24910 switch( inEvent )
24911 {
24912 case mdns_event_error:
24913 FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
24914 mdns_interface_monitor_invalidate( monitor );
24915 exitCode = 1;
24916 break;
24917
24918 case mdns_event_invalidated:
24919 FPrintF( stdout, "Interface monitor invalidated.\n" );
24920 mdns_release( monitor );
24921 exit( exitCode );
24922
24923 default:
24924 FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
24925 break;
24926 }
24927 } );
24928 mdns_interface_monitor_set_update_handler( monitor,
24929 ^( __unused mdns_interface_flags_t inChangeFlags )
24930 {
24931 _InterfaceMonitorPrint( monitor );
24932 } );
24933
24934 _InterfaceMonitorPrint( monitor );
24935 mdns_interface_monitor_activate( monitor );
24936
24937 signal( SIGINT, SIG_IGN );
24938 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _InterfaceMonitorSignalHandler, monitor,
24939 &signalSource );
24940 require_noerr( err, exit );
24941 dispatch_resume( signalSource );
24942
24943 dispatch_main();
24944
24945 exit:
24946 if( err ) ErrQuit( 1, "error: %#m\n", err );
24947 }
24948
24949 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
24950 {
24951 FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
24952 }
24953
24954 static void _InterfaceMonitorSignalHandler( void *inContext )
24955 {
24956 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
24957 }
24958
24959 //===========================================================================================================================
24960 // QuerierCommand
24961 //===========================================================================================================================
24962
24963 typedef struct
24964 {
24965 dispatch_queue_t queue; // Serial queue for command's events.
24966 dispatch_semaphore_t doneSem; // Semaphore to signal when command is done.
24967 uint8_t * qname; // Name of record to query for.
24968 mdns_querier_t querier; // Querier.
24969 dispatch_source_t sourceSigInt; // Dispatch source for SIGINT.
24970 dispatch_source_t sourceSigTerm; // Dispatch source for SIGTERM.
24971 int32_t refCount; // Reference count.
24972 OSStatus error; // Command's error.
24973 uint32_t ifIndex; // Interface index for scoping.
24974 uint16_t qtype; // Type of record to query for.
24975 uint16_t qclass; // Class of record to query for.
24976 pid_t delegatorPID; // Delegator PID.
24977 uint8_t delegatorUUID[ 16 ]; // Delegator UUID.
24978 Boolean haveDelegatorPID; // True if delegatorPID is set.
24979 Boolean haveDelegatorUUID; // True if delegatorUUID is set.
24980 Boolean dnssecOK; // True if queries need an OPT record with the DO bit set.
24981 Boolean checkingDisabled; // True if queries need the CD bit set.
24982 Boolean done; // True if the command is done.
24983
24984 // Variables for resolver.
24985
24986 mdns_resolver_type_t resolverType; // Type of resolver to use.
24987 mdns_resolver_t resolver; // Resolver.
24988 CFMutableArrayRef serverAddrs; // Server addresses to use for resolver.
24989 char * providerName; // Provider name for resolver.
24990 char * urlPath; // URL path for resolver.
24991 Boolean noConnectionReuse; // True if connection reuse is to be disabled.
24992 Boolean squashCNAMEs; // True if CNAMEs should be squashed.
24993
24994 // Variables for DNS service manager.
24995
24996 mdns_dns_service_manager_t manager; // DNS service manager.
24997 mdns_dns_service_t service; // DNS service for query.
24998
24999 } QuerierCmd;
25000
25001 static OSStatus _QuerierCmdCreate( QuerierCmd **outCmd );
25002 static void _QuerierCmdRetain( QuerierCmd *inCmd );
25003 static void _QuerierCmdRelease( QuerierCmd *inCmd );
25004 static OSStatus _QuerierCmdRun( QuerierCmd *inCmd );
25005
25006 static void QuerierCommand( void )
25007 {
25008 OSStatus err;
25009 QuerierCmd * cmd = NULL;
25010 size_t i;
25011 uint8_t qname[ kDomainNameLengthMax ];
25012
25013 err = _QuerierCmdCreate( &cmd );
25014 require_noerr( err, exit );
25015
25016 if( gInterface )
25017 {
25018 err = InterfaceIndexFromArgString( gInterface, &cmd->ifIndex );
25019 require_noerr_quiet( err, exit );
25020 }
25021 else
25022 {
25023 cmd->ifIndex = 0;
25024 }
25025 err = DomainNameFromString( qname, gQuerier_Name, NULL );
25026 if( err )
25027 {
25028 FPrintF( stderr, "error: Invalid domain name: '%s'\n", gDNSQuery_Name );
25029 goto exit;
25030 }
25031 err = DomainNameDup( qname, &cmd->qname, NULL );
25032 require_noerr( err, exit );
25033
25034 err = RecordTypeFromArgString( gQuerier_Type, &cmd->qtype );
25035 require_noerr_quiet( err, exit );
25036
25037 err = RecordClassFromArgString( gQuerier_Class, &cmd->qclass );
25038 require_noerr( err, exit );
25039
25040 if( gQuerier_Delegator )
25041 {
25042 err = StringToUUID( gQuerier_Delegator, kSizeCString, false, cmd->delegatorUUID );
25043 if( !err )
25044 {
25045 cmd->haveDelegatorUUID = true;
25046 }
25047 else
25048 {
25049 cmd->delegatorPID = _StringToPID( gQuerier_Delegator, &err );
25050 if( err )
25051 {
25052 FPrintF( stderr, "error: Invalid delegator PID or UUID: %s\n", gQuerier_Delegator );
25053 err = kParamErr;
25054 goto exit;
25055 }
25056 cmd->haveDelegatorPID = true;
25057 }
25058 }
25059 if( gQuerier_ResolverType )
25060 {
25061 cmd->resolverType = (mdns_resolver_type_t) CLIArgToValue( "resolverType", gQuerier_ResolverType, &err,
25062 kMDNSResolverTypeStr_Normal, (int) mdns_resolver_type_normal,
25063 kMDNSResolverTypeStr_TCPOnly, (int) mdns_resolver_type_tcp,
25064 kMDNSResolverTypeStr_TLS, (int) mdns_resolver_type_tls,
25065 kMDNSResolverTypeStr_HTTPS, (int) mdns_resolver_type_https,
25066 NULL );
25067 require_noerr_quiet( err, exit );
25068
25069 for( i = 0; i < gQuerier_ServerAddrCount; ++i )
25070 {
25071 const char * const addrStr = gQuerier_ServerAddrs[ i ];
25072 mdns_address_t serverAddr;
25073
25074 serverAddr = mdns_address_create_from_ip_address_string( addrStr );
25075 if( !serverAddr )
25076 {
25077 FPrintF( stderr, "error: Failed to create address for '%s'\n", addrStr );
25078 err = kParamErr;
25079 goto exit;
25080 }
25081 CFArrayAppendValue( cmd->serverAddrs, serverAddr );
25082 mdns_release( serverAddr );
25083 }
25084 if( gQuerier_ProviderName )
25085 {
25086 cmd->providerName = strdup( gQuerier_ProviderName );
25087 require_action( cmd->providerName, exit, err = kNoMemoryErr );
25088 }
25089 if( gQuerier_URLPath )
25090 {
25091 cmd->urlPath = strdup( gQuerier_URLPath );
25092 require_action( cmd->urlPath, exit, err = kNoMemoryErr );
25093 }
25094 cmd->noConnectionReuse = gQuerier_NoConnectionReuse ? true : false;
25095 cmd->squashCNAMEs = gQuerier_SquashCNAMEs ? true : false;
25096 }
25097 cmd->dnssecOK = gQuerier_DNSSECOK ? true : false;
25098 cmd->checkingDisabled = gQuerier_CheckingDisabled ? true : false;
25099 err = _QuerierCmdRun( cmd );
25100 require_noerr( err, exit );
25101
25102 exit:
25103 if( cmd ) _QuerierCmdRelease( cmd );
25104 gExitCode = err ? 1 : 0;
25105 }
25106
25107 //===========================================================================================================================
25108
25109 static OSStatus _QuerierCmdCreate( QuerierCmd **outCmd )
25110 {
25111 OSStatus err;
25112 QuerierCmd * cmd;
25113
25114 cmd = (QuerierCmd *) calloc( 1, sizeof( *cmd ) );
25115 require_action( cmd, exit, err = kNoResourcesErr );
25116
25117 cmd->refCount = 1;
25118 cmd->resolverType = mdns_resolver_type_null;
25119
25120 cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.querier-command", DISPATCH_QUEUE_SERIAL );
25121 require_action( cmd->queue, exit, err = kNoResourcesErr );
25122
25123 cmd->doneSem = dispatch_semaphore_create( 0 );
25124 require_action( cmd->doneSem, exit, err = kNoResourcesErr );
25125
25126 cmd->serverAddrs = CFArrayCreateMutable( kCFAllocatorDefault, 0, &mdns_cfarray_callbacks );
25127 require_action( cmd->serverAddrs, exit, err = kNoResourcesErr );
25128
25129 *outCmd = cmd;
25130 cmd = NULL;
25131 err = kNoErr;
25132
25133 exit:
25134 if( cmd ) _QuerierCmdRelease( cmd );
25135 return( err );
25136 }
25137
25138 //===========================================================================================================================
25139
25140 static void _QuerierCmdRetain( QuerierCmd *inCmd )
25141 {
25142 atomic_add_32( &inCmd->refCount, 1 );
25143 }
25144
25145 //===========================================================================================================================
25146
25147 static void _QuerierCmdRelease( QuerierCmd *inCmd )
25148 {
25149 if( atomic_add_and_fetch_32( &inCmd->refCount, -1 ) == 0 )
25150 {
25151 check( !inCmd->sourceSigInt );
25152 check( !inCmd->sourceSigTerm );
25153 check( !inCmd->resolver );
25154 check( !inCmd->manager );
25155 check( !inCmd->service );
25156 check( !inCmd->querier );
25157 dispatch_forget( &inCmd->queue );
25158 dispatch_forget( &inCmd->doneSem );
25159 ForgetMem( &inCmd->qname );
25160 ForgetCF( &inCmd->serverAddrs );
25161 ForgetMem( &inCmd->providerName );
25162 ForgetMem( &inCmd->urlPath );
25163 free( inCmd );
25164 }
25165 }
25166
25167 //===========================================================================================================================
25168
25169 static void _QuerierCmdStart( void *inCtx );
25170 static void _QuerierCmdStop( QuerierCmd *inCmd, OSStatus inError );
25171 static void _QuerierCmdSigIntHandler( void *inCtx );
25172 static void _QuerierCmdSigTermHandler( void *inCtx );
25173
25174 static OSStatus _QuerierCmdRun( QuerierCmd *inCmd )
25175 {
25176 dispatch_async_f( inCmd->queue, inCmd, _QuerierCmdStart );
25177 dispatch_semaphore_wait( inCmd->doneSem, DISPATCH_TIME_FOREVER );
25178 return( inCmd->error );
25179 }
25180
25181 static void _QuerierCmdStart( void *inCtx )
25182 {
25183 OSStatus err;
25184 QuerierCmd * const cmd = (QuerierCmd *) inCtx;
25185 dns_config_t * config = NULL;
25186 mdns_querier_t querier = NULL;
25187 const char * ifNamePtr;
25188 char ifNameBuf[ IF_NAMESIZE + 1 ];
25189
25190 signal( SIGINT, SIG_IGN );
25191 err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _QuerierCmdSigIntHandler, cmd, &cmd->sourceSigInt );
25192 require_noerr( err, exit );
25193 dispatch_resume( cmd->sourceSigInt );
25194
25195 signal( SIGTERM, SIG_IGN );
25196 err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _QuerierCmdSigTermHandler, cmd, &cmd->sourceSigTerm );
25197 require_noerr( err, exit );
25198 dispatch_resume( cmd->sourceSigTerm );
25199
25200 ifNamePtr = if_indextoname( cmd->ifIndex, ifNameBuf );
25201 FPrintF( stdout, "Interface: %u (%s)\n", cmd->ifIndex, ifNamePtr ? ifNamePtr : "?" );
25202 FPrintF( stdout, "Name: %{du:dname}\n", cmd->qname );
25203 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( cmd->qtype ), cmd->qtype );
25204 FPrintF( stdout, "Class: %s (%u)\n", RecordClassToString( cmd->qclass ), cmd->qclass );
25205 if( cmd->resolverType != mdns_resolver_type_null )
25206 {
25207 CFIndex n, i;
25208
25209 FPrintF( stdout, "Resolver Type: %s\n", mdns_resolver_type_to_string( cmd->resolverType ) );
25210 if( cmd->providerName ) FPrintF( stdout, "Provider Name: %s\n", cmd->providerName );
25211 if( cmd->urlPath ) FPrintF( stdout, "URL path: %s\n", cmd->urlPath );
25212 FPrintF( stdout, "Server(s): " );
25213 n = CFArrayGetCount( cmd->serverAddrs );
25214 for( i = 0; i < n; ++i )
25215 {
25216 FPrintF( stdout, "%s%@", ( i == 0 ) ? "" : ", ", CFArrayGetValueAtIndex( cmd->serverAddrs, i ) );
25217 }
25218 FPrintF( stdout, "\n" );
25219 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
25220 FPrintF( stdout, "---\n" );
25221
25222 cmd->resolver = mdns_resolver_create( cmd->resolverType, cmd->ifIndex, &err );
25223 require_noerr( err, exit );
25224
25225 if( cmd->providerName )
25226 {
25227 err = mdns_resolver_set_provider_name( cmd->resolver, cmd->providerName );
25228 require_noerr( err, exit );
25229 }
25230 if( cmd->urlPath )
25231 {
25232 err = mdns_resolver_set_url_path( cmd->resolver, cmd->urlPath );
25233 require_noerr( err, exit );
25234 }
25235 if( cmd->noConnectionReuse ) mdns_resolver_disable_connection_reuse( cmd->resolver, true );
25236 if( cmd->squashCNAMEs ) mdns_resolver_set_squash_cnames( cmd->resolver, true );
25237 for( i = 0; i < n; ++i )
25238 {
25239 const mdns_address_t addr = (mdns_address_t) CFArrayGetValueAtIndex( cmd->serverAddrs, i );
25240
25241 err = mdns_resolver_add_server_address( cmd->resolver, addr );
25242 require_noerr( err, exit );
25243 }
25244 mdns_resolver_activate( cmd->resolver );
25245
25246 querier = mdns_resolver_create_querier( cmd->resolver, &err );
25247 require_noerr( err, exit );
25248 }
25249 else
25250 {
25251 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
25252 FPrintF( stdout, "---\n" );
25253
25254 config = dns_configuration_copy();
25255 require_action( config, exit, err = kUnknownErr );
25256
25257 cmd->manager = mdns_dns_service_manager_create( cmd->queue, &err );
25258 require_noerr( err, exit );
25259
25260 _QuerierCmdRetain( cmd );
25261 mdns_dns_service_manager_set_event_handler( cmd->manager,
25262 ^( mdns_event_t inEvent, OSStatus inError )
25263 {
25264 switch( inEvent )
25265 {
25266 case mdns_event_error:
25267 if( !cmd->done )
25268 {
25269 FPrintF( stderr, "error: DNS service manager failed: %#m\n", inError );
25270 _QuerierCmdStop( cmd, inError );
25271 }
25272 break;
25273
25274 case mdns_event_invalidated:
25275 _QuerierCmdRelease( cmd );
25276 break;
25277
25278 default:
25279 break;
25280 }
25281 } );
25282 mdns_dns_service_manager_apply_dns_config( cmd->manager, config );
25283
25284 if( cmd->ifIndex == 0 )
25285 {
25286 cmd->service = mdns_dns_service_manager_get_unscoped_service( cmd->manager, cmd->qname );
25287 }
25288 else
25289 {
25290 cmd->service = mdns_dns_service_manager_get_interface_scoped_service( cmd->manager, cmd->qname, cmd->ifIndex );
25291 }
25292 if( !cmd->service )
25293 {
25294 FPrintF( stderr, "error: Failed to get DNS service for %{du:dname}\n", cmd->qname );
25295 err = kNotFoundErr;
25296 goto exit;
25297 }
25298 mdns_retain( cmd->service );
25299 FPrintF( stdout, "Using DNS service: %@\n\n", cmd->service );
25300
25301 querier = mdns_dns_service_create_querier( cmd->service, &err );
25302 require_noerr( err, exit );
25303 }
25304 err = mdns_querier_set_query( querier, cmd->qname, cmd->qtype, cmd->qclass );
25305 require_noerr( err, exit );
25306
25307 if( cmd->dnssecOK ) mdns_querier_set_dnssec_ok( querier, true );
25308 if( cmd->checkingDisabled ) mdns_querier_set_checking_disabled( querier, true );
25309 cmd->querier = querier;
25310 querier = NULL;
25311
25312 if( cmd->haveDelegatorPID ) mdns_querier_set_delegator_pid( cmd->querier, cmd->delegatorPID );
25313 else if( cmd->haveDelegatorUUID ) mdns_querier_set_delegator_uuid( cmd->querier, cmd->delegatorUUID );
25314
25315 _QuerierCmdRetain( cmd );
25316 mdns_querier_set_queue( cmd->querier, cmd->queue );
25317 mdns_querier_set_result_handler( cmd->querier,
25318 ^ {
25319 if( !cmd->done )
25320 {
25321 const mdns_querier_result_type_t resultType = mdns_querier_get_result_type( cmd->querier );
25322
25323 if( resultType == mdns_querier_result_type_response )
25324 {
25325 const uint8_t * const msgPtr = mdns_querier_get_response_ptr( cmd->querier );
25326 const size_t msgLen = mdns_querier_get_response_length( cmd->querier );
25327
25328 FPrintF( stdout, "Message size: %zu bytes\n", msgLen );
25329 FPrintF( stdout, "%{du:dnsmsg}\n", msgPtr, msgLen );
25330 _QuerierCmdStop( cmd, kNoErr );
25331 }
25332 else
25333 {
25334 OSStatus querierErr;
25335
25336 if( resultType == mdns_querier_result_type_error )
25337 {
25338 querierErr = mdns_querier_get_error( cmd->querier );
25339 if( !querierErr ) querierErr = kUnknownErr;
25340 }
25341 else
25342 {
25343 querierErr = kUnexpectedErr;
25344 }
25345 FPrintF( stderr, "error: Unexpected querier result: %s, error: %#m\n",
25346 mdns_querier_result_type_to_string( resultType ), querierErr );
25347 _QuerierCmdStop( cmd, querierErr );
25348 }
25349 }
25350 _QuerierCmdRelease( cmd );
25351 } );
25352 mdns_querier_activate( cmd->querier );
25353
25354 exit:
25355 if( config ) dns_configuration_free( config );
25356 mdns_release_null_safe( querier );
25357 if( err ) _QuerierCmdStop( cmd, err );
25358 }
25359
25360 //===========================================================================================================================
25361
25362 #define mdns_dns_service_manager_forget( X ) ForgetCustomEx( X, mdns_dns_service_manager_invalidate, mdns_release )
25363
25364 static void _QuerierCmdStop( QuerierCmd *inCmd, OSStatus inError )
25365 {
25366 if( !inCmd->done )
25367 {
25368 inCmd->done = true;
25369 inCmd->error = inError;
25370 dispatch_source_forget( &inCmd->sourceSigInt );
25371 dispatch_source_forget( &inCmd->sourceSigTerm );
25372 mdns_querier_forget( &inCmd->querier );
25373 mdns_resolver_forget( &inCmd->resolver );
25374 mdns_dns_service_manager_forget( &inCmd->manager );
25375 mdns_forget( &inCmd->service );
25376 FPrintF( stdout, "---\n" );
25377 FPrintF( stdout, "End time: %{du:time}\n", NULL );
25378 dispatch_semaphore_signal( inCmd->doneSem );
25379 }
25380 }
25381
25382 //===========================================================================================================================
25383
25384 static void _QuerierCmdSigIntHandler( void *inCtx )
25385 {
25386 FPrintF( stdout, "*** Got SIGINIT signal ***\n" );
25387 _QuerierCmdStop( (QuerierCmd *) inCtx, kCanceledErr );
25388 }
25389
25390 //===========================================================================================================================
25391
25392 static void _QuerierCmdSigTermHandler( void *inCtx )
25393 {
25394 FPrintF( stdout, "*** Got SIGTERM signal ***\n" );
25395 _QuerierCmdStop( (QuerierCmd *) inCtx, kCanceledErr );
25396 }
25397
25398 //===========================================================================================================================
25399 // DNSProxyCmd
25400 //===========================================================================================================================
25401
25402 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
25403 static void _DNSProxyCmdSignalHandler( void *inContext );
25404
25405 static void DNSProxyCmd( void )
25406 {
25407 OSStatus err;
25408 size_t i;
25409 DNSXConnRef connection;
25410 IfIndex inputIfIndexes[ MaxInputIf ];
25411 dispatch_source_t sigIntSource = NULL;
25412 dispatch_source_t sigTermSource = NULL;
25413 uint32_t outputIfIndex;
25414 char ifName[ kInterfaceNameBufLen ];
25415 uint8_t dns64Prefix[ 16 ];
25416 int dns64PrefixBitLen;
25417
25418 if( gDNSProxy_InputInterfaceCount > MaxInputIf )
25419 {
25420 FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
25421 gDNSProxy_InputInterfaceCount, MaxInputIf );
25422 err = kRangeErr;
25423 goto exit;
25424 }
25425
25426 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
25427 {
25428 uint32_t ifIndex;
25429
25430 err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
25431 require_noerr_quiet( err, exit );
25432
25433 inputIfIndexes[ i ] = ifIndex;
25434 }
25435 while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0; // Remaining interface indexes are required to be 0.
25436
25437 if( gDNSProxy_OutputInterface )
25438 {
25439 err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
25440 require_noerr_quiet( err, exit );
25441 }
25442 else
25443 {
25444 outputIfIndex = kDNSIfindexAny;
25445 }
25446
25447 dns64PrefixBitLen = 0;
25448 if( gDNSProxy_DNS64IPv6Prefix )
25449 {
25450 const char * end;
25451
25452 err = _StringToIPv6Address( gDNSProxy_DNS64IPv6Prefix,
25453 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoScope, dns64Prefix, NULL, NULL, &dns64PrefixBitLen,
25454 &end );
25455 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
25456 require_noerr_quiet( err, exit );
25457 }
25458 FPrintF( stdout, "Input Interfaces:" );
25459 for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
25460 {
25461 const uint32_t ifIndex = (uint32_t) inputIfIndexes[ i ];
25462
25463 FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
25464 }
25465 FPrintF( stdout, "\n" );
25466 FPrintF( stdout, "Output Interface: %u (%s)\n", outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
25467 if( gDNSProxy_DNS64IPv6Prefix ) FPrintF( stdout, "DNS64 prefix: %.16a/%d\n", dns64Prefix, dns64PrefixBitLen );
25468 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
25469 FPrintF( stdout, "---\n" );
25470
25471 connection = NULL;
25472 if( gDNSProxy_DNS64IPv6Prefix )
25473 {
25474 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25475 {
25476 err = DNSXEnableProxy64( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex,
25477 dns64Prefix, dns64PrefixBitLen, kDNSXProxyFlagNull, dispatch_get_main_queue(), _DNSProxyCallback );
25478 require_noerr_quiet( err, exit );
25479 }
25480 else
25481 {
25482 FPrintF( stderr, "error: DNSXEnableProxy64() is not available on this OS.\n" );
25483 err = kUnsupportedErr;
25484 goto exit;
25485 }
25486 }
25487 else
25488 {
25489 err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex,
25490 dispatch_get_main_queue(), _DNSProxyCallback );
25491 require_noerr_quiet( err, exit );
25492 }
25493 signal( SIGINT, SIG_IGN );
25494 err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
25495 &sigIntSource );
25496 require_noerr( err, exit );
25497 dispatch_activate( sigIntSource );
25498
25499 signal( SIGTERM, SIG_IGN );
25500 err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
25501 &sigTermSource );
25502 require_noerr( err, exit );
25503 dispatch_activate( sigTermSource );
25504
25505 dispatch_main();
25506
25507 exit:
25508 if( err ) ErrQuit( 1, "error: %#m\n", err );
25509 }
25510
25511 static void _DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
25512 {
25513 Unused( inConnection );
25514
25515 if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
25516 }
25517
25518 static void _DNSProxyCmdSignalHandler( void *inContext )
25519 {
25520 DNSXConnRef const connection = (DNSXConnRef) inContext;
25521 struct timeval now;
25522
25523 gettimeofday( &now, NULL );
25524
25525 DNSXRefDeAlloc( connection );
25526
25527 FPrintF( stdout, "---\n" );
25528 FPrintF( stdout, "End time: %{du:time}\n", &now );
25529 exit( 0 );
25530 }
25531
25532 //===========================================================================================================================
25533 // GetAddrInfoNewCommand
25534 //===========================================================================================================================
25535
25536
25537 typedef enum
25538 {
25539 kDelegationType_None = 0, // No delegation.
25540 kDelegationType_PID = 1, // Delegation by PID.
25541 kDelegationType_UUID = 2, // Delegation by UUID.
25542 kDelegationType_AuditToken = 3 // Delegation by audit token.
25543
25544 } DelegationType;
25545
25546 typedef struct
25547 {
25548 DelegationType type; // Type of delegation.
25549 union
25550 {
25551 pid_t pid; // Delegator's PID if type is kDelegationType_PID.
25552 uuid_t uuid; // Delegator's UUID if type is kDelegationType_UUID.
25553 audit_token_t auditToken; // Delegator's audit token if type is kDelegationType_AuditToken.
25554
25555 } ident; // Delegator's identifier.
25556
25557 } Delegation;
25558
25559 typedef struct
25560 {
25561 dispatch_queue_t queue; // Serial queue for command's events.
25562 dispatch_group_t group; // GCD group to know when command is done.
25563 dnssd_getaddrinfo_t gai; // dnssd_getaddrinfo object.
25564 dispatch_source_t timer; // Timer to impose time limit on dnssd_getaddrinfo activity.
25565 dispatch_source_t sigint; // Dispatch source for SIGINT.
25566 dispatch_source_t sigterm; // Dispatch source for SIGTERM.
25567 const char * hostname; // dnssd_getaddrinfo's hostname argument.
25568 const char * serviceScheme; // dnssd_getaddrinfo's service scheme argument.
25569 const char * accountID; // dnssd_getaddrinfo's account ID argument.
25570 char * stopReason; // Reason for stopping the command.
25571 Delegation delegation; // Specifies the type of delegation to use for dnssd_getaddrinfo, if any.
25572 int32_t refCount; // Reference count.
25573 DNSServiceFlags flags; // dnssd_getaddrinfo's flags argument.
25574 DNSServiceProtocol protocols; // dnssd_getaddrinfo's protocols argument.
25575 uint32_t ifIndex; // dnssd_getaddrinfo's interface index argument.
25576 unsigned int timeLimitSecs; // Time limit in seconds for dnssd_getaddrinfo activity.
25577 OSStatus error; // Command's error.
25578 Boolean needAuthTags; // True if dnssd_getaddrinfo
25579 Boolean stopped; // True if the command has been stopped.
25580 Boolean oneshot; // True if the command should stop after first set of results.
25581 Boolean printedHeader; // True if the results header has been printed.
25582
25583 } GetAddrInfoNewCmd;
25584
25585 static GetAddrInfoNewCmd * _GetAddrInfoNewCmdCreateEx( qos_class_t inQoS, Boolean inUseQoS, OSStatus *outError );
25586 #define _GetAddrInfoNewCmdCreate( OUT_ERROR ) _GetAddrInfoNewCmdCreateEx( 0, false, OUT_ERROR )
25587 #define _GetAddrInfoNewCmdCreateWithQoS( IN_QOS, OUT_ERROR ) _GetAddrInfoNewCmdCreateEx( IN_QOS, true, OUT_ERROR )
25588 static OSStatus _GetAddrInfoNewCmdRun( GetAddrInfoNewCmd *inCmd );
25589 static void _GetAddrInfoNewCmdStopF( GetAddrInfoNewCmd *inCmd, const char *inFmt, ... );
25590 static GetAddrInfoNewCmd * _GetAddrInfoNewCmdRetain( GetAddrInfoNewCmd *inCmd );
25591 static void _GetAddrInfoNewCmdRelease( GetAddrInfoNewCmd *inCmd );
25592
25593 #define _GetAddrInfoNewCmdForget( X ) ForgetCustom( X, _GetAddrInfoNewCmdRelease )
25594
25595 static void GetAddrInfoNewCommand( void )
25596 {
25597 OSStatus err;
25598 GetAddrInfoNewCmd * cmd = NULL;
25599
25600 if( gGAINew_QoS )
25601 {
25602 qos_class_t qos;
25603
25604 qos = (qos_class_t) CLIArgToValue( kQoSArgShortName, gGAINew_QoS, &err,
25605 kQoSTypeStr_Unspecified, QOS_CLASS_UNSPECIFIED,
25606 kQoSTypeStr_Background, QOS_CLASS_BACKGROUND,
25607 kQoSTypeStr_Utility, QOS_CLASS_UTILITY,
25608 kQoSTypeStr_Default, QOS_CLASS_DEFAULT,
25609 kQoSTypeStr_UserInitiated, QOS_CLASS_USER_INITIATED,
25610 kQoSTypeStr_UserInteractive, QOS_CLASS_USER_INTERACTIVE,
25611 NULL );
25612 require_noerr_quiet( err, exit );
25613
25614 cmd = _GetAddrInfoNewCmdCreateWithQoS( qos, &err );
25615 require_noerr( err, exit );
25616 }
25617 else
25618 {
25619 cmd = _GetAddrInfoNewCmdCreate( &err );
25620 require_noerr( err, exit );
25621 }
25622 err = CheckIntegerArgument( gGAINew_TimeLimitSecs, "time limit", 0, INT_MAX );
25623 require_noerr_quiet( err, exit );
25624
25625 cmd->timeLimitSecs = (unsigned int) gGAINew_TimeLimitSecs;
25626
25627 cmd->hostname = gGAINew_Hostname;
25628 cmd->flags = GetDNSSDFlagsFromOpts();
25629 cmd->serviceScheme = gGAINew_ServiceScheme;
25630 cmd->accountID = gGAINew_AccountID;
25631 cmd->needAuthTags = gGAINew_WantAuthTags ? true : false;
25632 cmd->oneshot = gGAINew_OneShot ? true : false;
25633
25634 err = InterfaceIndexFromArgString( gInterface, &cmd->ifIndex );
25635 require_noerr_quiet( err, exit );
25636
25637 cmd->protocols = 0;
25638 if( gGAINew_ProtocolIPv4 ) cmd->protocols |= kDNSServiceProtocol_IPv4;
25639 if( gGAINew_ProtocolIPv6 ) cmd->protocols |= kDNSServiceProtocol_IPv6;
25640
25641 // Get delegate ID.
25642
25643 if( gGAINew_DelegatorID )
25644 {
25645 pid_t delegatorPID;
25646
25647 delegatorPID = _StringToPID( gGAINew_DelegatorID, &err );
25648 if( !err )
25649 {
25650 if( delegatorPID >= 0 )
25651 {
25652 cmd->delegation.ident.pid = delegatorPID;
25653 cmd->delegation.type = kDelegationType_PID;
25654 }
25655 else
25656 {
25657 delegatorPID = -delegatorPID;
25658 if( audit_token_for_pid( delegatorPID, &cmd->delegation.ident.auditToken ) )
25659 {
25660 cmd->delegation.type = kDelegationType_AuditToken;
25661 }
25662 else
25663 {
25664 FPrintF( stderr, "Failed to get audit token for PID: %d\n", delegatorPID );
25665 err = kParamErr;
25666 goto exit;
25667 }
25668 }
25669 }
25670 else
25671 {
25672 err = uuid_parse( gGAINew_DelegatorID, cmd->delegation.ident.uuid );
25673 if( !err )
25674 {
25675 cmd->delegation.type = kDelegationType_UUID;
25676 }
25677 else
25678 {
25679 FPrintF( stderr, "Invalid delegate ID (PID or UUID): %s\n", gGAINew_DelegatorID );
25680 err = kParamErr;
25681 goto exit;
25682 }
25683 }
25684 }
25685 err = _GetAddrInfoNewCmdRun( cmd );
25686 require_noerr( err, exit );
25687
25688 exit:
25689 _GetAddrInfoNewCmdForget( &cmd );
25690 gExitCode = err ? 1 : 0;
25691 }
25692
25693 //===========================================================================================================================
25694
25695 static GetAddrInfoNewCmd * _GetAddrInfoNewCmdCreateEx( qos_class_t inQoS, Boolean inUseQoS, OSStatus *outError )
25696 {
25697 OSStatus err;
25698 GetAddrInfoNewCmd * obj;
25699 GetAddrInfoNewCmd * cmd = NULL;
25700
25701 obj = (GetAddrInfoNewCmd *) calloc( 1, sizeof( *obj ) );
25702 require_action( obj, exit, err = kNoMemoryErr );
25703
25704 obj->refCount = 1;
25705 if( inUseQoS )
25706 {
25707 dispatch_queue_attr_t attr;
25708
25709 attr = dispatch_queue_attr_make_with_qos_class( DISPATCH_QUEUE_SERIAL, inQoS, 0 );
25710 obj->queue = dispatch_queue_create( "com.apple.dnssdutil.getaddrinfo-new-command", attr );
25711 require_action( obj->queue, exit, err = kNoResourcesErr );
25712 }
25713 else
25714 {
25715 obj->queue = dispatch_queue_create( "com.apple.dnssdutil.getaddrinfo-new-command", DISPATCH_QUEUE_SERIAL );
25716 require_action( obj->queue, exit, err = kNoResourcesErr );
25717 }
25718 obj->group = dispatch_group_create();
25719 require_action( obj->group, exit, err = kNoResourcesErr );
25720
25721 cmd = obj;
25722 obj = NULL;
25723 err = kNoErr;
25724
25725 exit:
25726 if( outError ) *outError = err;
25727 _GetAddrInfoNewCmdForget( &obj );
25728 return( cmd );
25729 }
25730
25731 //===========================================================================================================================
25732
25733 static void _GetAddrInfoNewCmdStart( void *inCtx );
25734
25735 static OSStatus _GetAddrInfoNewCmdRun( GetAddrInfoNewCmd *me )
25736 {
25737 dispatch_group_enter( me->group );
25738 dispatch_async_f( me->queue, me, _GetAddrInfoNewCmdStart );
25739 dispatch_group_wait( me->group, DISPATCH_TIME_FOREVER );
25740 FPrintF( stdout, "---\n" );
25741 FPrintF( stdout, "End time: %{du:time}\n", NULL );
25742 if( me->stopReason ) FPrintF( stdout, "End reason: %s\n", me->stopReason );
25743 return( me->error );
25744 }
25745
25746 static void _GetAddrInfoNewCmdStart( void *inCtx )
25747 {
25748 OSStatus err;
25749 GetAddrInfoNewCmd * const me = (GetAddrInfoNewCmd *) inCtx;
25750 char ifName[ kInterfaceNameBufLen ];
25751
25752 me->gai = dnssd_getaddrinfo_create();
25753 require_action( me->gai, exit, err = kNoResourcesErr );
25754
25755 dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
25756 dnssd_getaddrinfo_set_flags( me->gai, me->flags );
25757 dnssd_getaddrinfo_set_interface_index( me->gai, me->ifIndex );
25758 dnssd_getaddrinfo_set_protocols( me->gai, me->protocols );
25759 dnssd_getaddrinfo_set_need_authenticated_results( me->gai, me->needAuthTags ? true : false );
25760 switch( me->delegation.type )
25761 {
25762 case kDelegationType_PID:
25763 dnssd_getaddrinfo_set_delegate_pid( me->gai, me->delegation.ident.pid );
25764 break;
25765
25766 case kDelegationType_UUID:
25767 dnssd_getaddrinfo_set_delegate_uuid( me->gai, me->delegation.ident.uuid );
25768 break;
25769
25770 case kDelegationType_AuditToken:
25771 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25772 {
25773 dnssd_getaddrinfo_set_delegate_audit_token( me->gai, me->delegation.ident.auditToken );
25774 }
25775 else
25776 {
25777 FPrintF( stderr, "error: dnssd_getaddrinfo_set_delegate_audit_token() is not available on this OS.\n" );
25778 err = kUnsupportedErr;
25779 goto exit;
25780 }
25781 break;
25782
25783 case kDelegationType_None:
25784 default:
25785 break;
25786 }
25787 if( me->serviceScheme )
25788 {
25789 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25790 {
25791 dnssd_getaddrinfo_set_service_scheme( me->gai, me->serviceScheme );
25792 }
25793 else
25794 {
25795 FPrintF( stderr, "error: dnssd_getaddrinfo_set_service_scheme() is not available on this OS.\n" );
25796 err = kUnsupportedErr;
25797 goto exit;
25798 }
25799 }
25800 if( me->accountID )
25801 {
25802 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25803 {
25804 dnssd_getaddrinfo_set_account_id( me->gai, me->accountID );
25805 }
25806 else
25807 {
25808 FPrintF( stderr, "error: dnssd_getaddrinfo_set_account_id() is not available on this OS.\n" );
25809 err = kUnsupportedErr;
25810 goto exit;
25811 }
25812 }
25813 dnssd_getaddrinfo_set_queue( me->gai, me->queue );
25814 dnssd_getaddrinfo_set_result_handler( me->gai,
25815 ^( dnssd_getaddrinfo_result_t *inResultArray, size_t inResultCount )
25816 {
25817 if( !me->gai ) return;
25818 if( inResultCount > 0 )
25819 {
25820 size_t i;
25821
25822 for( i = 0; i < inResultCount; ++i )
25823 {
25824 const dnssd_getaddrinfo_result_t result = inResultArray[ i ];
25825 const dnssd_getaddrinfo_result_type_t type = dnssd_getaddrinfo_result_get_type( result );
25826 const char * typeStr;
25827 const char * cacheStr;
25828
25829 if( !me->printedHeader )
25830 {
25831 FPrintF( stdout, "%-26s Type C? IF %-30s Address\n", "Timestamp", "Hostname" );
25832 me->printedHeader = true;
25833 }
25834 switch( type )
25835 {
25836 case dnssd_getaddrinfo_result_type_add: typeStr = "Add"; break;
25837 case dnssd_getaddrinfo_result_type_remove: typeStr = "Rmv"; break;
25838 case dnssd_getaddrinfo_result_type_no_address: typeStr = "NoA"; break;
25839 case dnssd_getaddrinfo_result_type_expired: typeStr = "Exp"; break;
25840 case dnssd_getaddrinfo_result_type_service_binding: typeStr = "SvB"; break;
25841 default: typeStr = "???"; break;
25842 }
25843 cacheStr = "-";
25844 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25845 {
25846 if( type != dnssd_getaddrinfo_result_type_remove )
25847 {
25848 cacheStr = dnssd_getaddrinfo_result_is_from_cache( result ) ? "Y" : "N";
25849 }
25850 }
25851 FPrintF( stdout, "%{du:time} %-4s %-2s %2d %-30s %##a\n",
25852 NULL, typeStr, cacheStr, dnssd_getaddrinfo_result_get_interface_index( result ),
25853 dnssd_getaddrinfo_result_get_hostname( result ), dnssd_getaddrinfo_result_get_address( result ) );
25854 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25855 {
25856 if( me->flags & kDNSServiceFlagsReturnIntermediates )
25857 {
25858 const dnssd_cname_array_t cnames = dnssd_getaddrinfo_result_get_cnames( result );
25859 size_t j, n;
25860
25861 FPrintF( stdout, " Canonical Names: [" );
25862 n = dnssd_cname_array_get_count( cnames );
25863 for( j = 0; j < n; ++j )
25864 {
25865 FPrintF( stdout, "%s%s", ( j == 0 ) ? "" : ", ", dnssd_cname_array_get_cname( cnames, j ) );
25866 }
25867 FPrintF( stdout, "]\n" );
25868 }
25869 }
25870 if( me->needAuthTags )
25871 {
25872 if( ( type == dnssd_getaddrinfo_result_type_add ) || ( type == dnssd_getaddrinfo_result_type_expired ) )
25873 {
25874 const void * tagPtr;
25875 size_t tagLen;
25876
25877 tagPtr = dnssd_getaddrinfo_result_get_authentication_tag( result, &tagLen );
25878 FPrintF( stdout, " Auth Tag: " );
25879 if( tagPtr ) FPrintF( stdout, "%.4H (%zu bytes)\n", tagPtr, (int) tagLen, (int) tagLen, tagLen );
25880 else FPrintF( stdout, "<NO AUTH TAG!>\n" );
25881 }
25882 }
25883 }
25884 if( me->oneshot ) _GetAddrInfoNewCmdStopF( me, "one-shot done" );
25885 }
25886 } );
25887 _GetAddrInfoNewCmdRetain( me );
25888 dispatch_group_enter( me->group );
25889 dnssd_getaddrinfo_set_event_handler( me->gai,
25890 ^( dnssd_event_t inEvent, DNSServiceErrorType inError )
25891 {
25892 switch( inEvent )
25893 {
25894 case dnssd_event_invalidated:
25895 dispatch_group_leave( me->group );
25896 _GetAddrInfoNewCmdRelease( me );
25897 break;
25898
25899 case dnssd_event_error:
25900 if( !me->error ) me->error = inError;
25901 _GetAddrInfoNewCmdStopF( me, "error %#m", inError );
25902 break;
25903
25904 default:
25905 break;
25906 }
25907 } );
25908
25909 // Set up signal handlers.
25910
25911 signal( SIGINT, SIG_IGN );
25912 me->sigint = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) SIGINT, 0, me->queue );
25913 require_action( me->sigint, exit, err = kNoResourcesErr );
25914
25915 dispatch_source_set_event_handler( me->sigint, ^{ _GetAddrInfoNewCmdStopF( me, "interrupt signal" ); } );
25916 dispatch_activate( me->sigint );
25917
25918 signal( SIGTERM, SIG_IGN );
25919 me->sigterm = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) SIGTERM, 0, me->queue );
25920 require_action( me->sigterm, exit, err = kNoResourcesErr );
25921
25922 dispatch_source_set_event_handler( me->sigterm, ^{ _GetAddrInfoNewCmdStopF( me, "termination signal" ); } );
25923 dispatch_activate( me->sigterm );
25924
25925 // Start getaddrinfo operation.
25926
25927 InterfaceIndexToName( me->ifIndex, ifName );
25928 FPrintF( stdout, "Service Scheme: %s\n", me->serviceScheme );
25929 FPrintF( stdout, "Flags: %#{flags}\n", me->flags, kDNSServiceFlagsDescriptors );
25930 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) me->ifIndex, ifName );
25931 FPrintF( stdout, "Protocols: %#{flags}\n", me->protocols, kDNSServiceProtocolDescriptors );
25932 FPrintF( stdout, "Name: %s\n", me->hostname );
25933 FPrintF( stdout, "Mode: %s\n", me->oneshot ? "one-shot" : "continuous" );
25934 if( me->serviceScheme ) FPrintF( stdout, "Service Scheme: %s\n", me->serviceScheme );
25935 if( me->accountID ) FPrintF( stdout, "Account ID: %s\n", me->accountID );
25936 FPrintF( stdout, "Time limit: " );
25937 if( me->timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", me->timeLimitSecs, me->timeLimitSecs != 1, 's' );
25938 else FPrintF( stdout, "∞\n" );
25939 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
25940 FPrintF( stdout, "---\n" );
25941
25942 if( me->timeLimitSecs > 0 )
25943 {
25944 me->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, me->queue );
25945 require_action( me->timer, exit, err = kNoResourcesErr );
25946
25947 dispatch_source_set_timer( me->timer, dispatch_time_seconds( me->timeLimitSecs ), DISPATCH_TIME_FOREVER,
25948 me->timeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 20 ) );
25949 dispatch_source_set_event_handler( me->timer, ^{ _GetAddrInfoNewCmdStopF( me, "time limit" ); } );
25950 dispatch_activate( me->timer );
25951 }
25952 dnssd_getaddrinfo_activate( me->gai );
25953 err = kNoErr;
25954
25955 exit:
25956 if( err ) _GetAddrInfoNewCmdStopF( me, "error %#m", err );
25957 }
25958
25959 //===========================================================================================================================
25960
25961 static void _GetAddrInfoNewCmdStopF( GetAddrInfoNewCmd *me, const char *inFmt, ... )
25962 {
25963 if( !me->stopped )
25964 {
25965 me->stopped = true;
25966 dnssd_getaddrinfo_forget( &me->gai );
25967 dispatch_source_forget( &me->timer );
25968 dispatch_source_forget( &me->sigint );
25969 dispatch_source_forget( &me->sigterm );
25970 if( inFmt )
25971 {
25972 va_list args;
25973
25974 check( !me->stopReason );
25975 va_start( args, inFmt );
25976 VASPrintF( &me->stopReason, inFmt, args );
25977 va_end( args );
25978 check( me->stopReason );
25979 }
25980 dispatch_group_leave( me->group );
25981 }
25982 }
25983
25984 //===========================================================================================================================
25985
25986 static GetAddrInfoNewCmd * _GetAddrInfoNewCmdRetain( GetAddrInfoNewCmd *me )
25987 {
25988 atomic_add_32( &me->refCount, 1 );
25989 return( me );
25990 }
25991
25992 //===========================================================================================================================
25993
25994 static void _GetAddrInfoNewCmdRelease( GetAddrInfoNewCmd *me )
25995 {
25996 if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
25997 {
25998 check( !me->gai );
25999 check( !me->timer );
26000 check( !me->sigint );
26001 check( !me->sigterm );
26002 dispatch_forget( &me->queue );
26003 dispatch_forget( &me->group );
26004 ForgetMem( &me->stopReason );
26005 free( me );
26006 }
26007 }
26008
26009 //===========================================================================================================================
26010 // XCTestCmd
26011 //===========================================================================================================================
26012
26013 static void XCTestCmd( void )
26014 {
26015 int result = 0;
26016 setenv(DNSSDUTIL_XCTEST, DNSSDUTIL_XCTEST, 0);
26017 if(!run_xctest_named(gXCTest_Classname)) {
26018 result = 1;
26019 }
26020 unsetenv(DNSSDUTIL_XCTEST);
26021 exit( result );
26022 }
26023
26024 //===========================================================================================================================
26025 // MultiConnectTestCmd
26026 //===========================================================================================================================
26027
26028 static void MultiConnectTestCmd( void )
26029 {
26030 int result = 0;
26031 dispatch_group_t group = dispatch_group_create();
26032 int count = gMultiConnectTest_ConnectionCount;
26033 DNSServiceRef * refs = calloc( (uint32_t)count, sizeof(DNSServiceRef) );
26034
26035 // Create count connections on async queue
26036 // rdar://problem/59861422
26037 __block int goodcount = 0;
26038 for ( int i = 0; i < count; i++ ) {
26039 char label[256];
26040 sprintf( label, "multi-connect queue %d", i+1 );
26041 dispatch_group_async( group, dispatch_queue_create( label, DISPATCH_QUEUE_SERIAL ), ^{
26042 DNSServiceErrorType err = DNSServiceCreateConnection( &refs[i] );
26043 if ( err ) {
26044 fprintf( stderr, "%s Failed to create connection # %d err(%d)\n", __FUNCTION__, i, err );
26045 } else {
26046 goodcount++;
26047 }
26048 });
26049 }
26050 dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
26051 dispatch_release( group );
26052 fprintf( stderr, "%s Created %d connections of %d\n", __FUNCTION__, goodcount, count );
26053
26054 // Free them
26055 goodcount = 0;
26056 for ( int i = 0; i < count; i++ ) {
26057 if ( refs[i] ) {
26058 DNSServiceRefDeallocate( refs[i] );
26059 goodcount++;
26060 }
26061 }
26062 fprintf( stderr, "%s Stopped %d connections of %d\n", __FUNCTION__, goodcount, count );
26063 result = (goodcount == count) ? 0 : 1;
26064 exit( result );
26065 }
26066
26067 #endif // MDNSRESPONDER_PROJECT
26068
26069 //===========================================================================================================================
26070 // DaemonVersionCmd
26071 //===========================================================================================================================
26072
26073 static void DaemonVersionCmd( void )
26074 {
26075 OSStatus err;
26076 uint32_t size, version;
26077 char strBuf[ 16 ];
26078
26079 size = (uint32_t) sizeof( version );
26080 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
26081 require_noerr( err, exit );
26082
26083 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
26084
26085 exit:
26086 if( err ) exit( 1 );
26087 }
26088
26089 //===========================================================================================================================
26090 // Exit
26091 //===========================================================================================================================
26092
26093 static void Exit( void *inContext )
26094 {
26095 const char * const reason = (const char *) inContext;
26096
26097 FPrintF( stdout, "---\n" );
26098 FPrintF( stdout, "End time: %{du:time}\n", NULL );
26099 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
26100 exit( gExitCode );
26101 }
26102
26103 //===========================================================================================================================
26104 // _PrintFExtensionHandler_Timestamp
26105 //===========================================================================================================================
26106
26107 static int
26108 _PrintFExtensionHandler_Timestamp(
26109 PrintFContext * inContext,
26110 PrintFFormat * inFormat,
26111 PrintFVAList * inArgs,
26112 void * inUserContext )
26113 {
26114 struct timeval now;
26115 const struct timeval * tv;
26116 struct tm * localTime;
26117 size_t len;
26118 int n;
26119 char dateTimeStr[ 32 ];
26120
26121 Unused( inUserContext );
26122
26123 tv = va_arg( inArgs->args, const struct timeval * );
26124 require_action_quiet( !inFormat->suppress, exit, n = 0 );
26125
26126 if( !tv )
26127 {
26128 gettimeofday( &now, NULL );
26129 tv = &now;
26130 }
26131 localTime = localtime( &tv->tv_sec );
26132 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
26133 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
26134
26135 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
26136
26137 exit:
26138 return( n );
26139 }
26140
26141 //===========================================================================================================================
26142 // _PrintFExtensionHandler_DNSMessage
26143 //===========================================================================================================================
26144
26145 static int
26146 _PrintFExtensionHandler_DNSMessageCommon(
26147 PrintFContext * inContext,
26148 PrintFFormat * inFormat,
26149 PrintFVAList * inArgs,
26150 void * inUserContext,
26151 Boolean inRawRData );
26152
26153 static int
26154 _PrintFExtensionHandler_DNSMessage(
26155 PrintFContext * inContext,
26156 PrintFFormat * inFormat,
26157 PrintFVAList * inArgs,
26158 void * inUserContext )
26159 {
26160 return( _PrintFExtensionHandler_DNSMessageCommon( inContext, inFormat, inArgs, inUserContext, false ) );
26161 }
26162
26163 static int
26164 _PrintFExtensionHandler_DNSMessageCommon(
26165 PrintFContext * inContext,
26166 PrintFFormat * inFormat,
26167 PrintFVAList * inArgs,
26168 void * inUserContext,
26169 Boolean inRawRData )
26170 {
26171 OSStatus err;
26172 const void * msgPtr;
26173 size_t msgLen;
26174 char * msgStr;
26175 DNSMessageToStringFlags flags;
26176 int n;
26177
26178 Unused( inUserContext );
26179
26180 msgPtr = va_arg( inArgs->args, const void * );
26181 msgLen = va_arg( inArgs->args, size_t );
26182 require_action_quiet( !inFormat->suppress, exit, n = 0 );
26183
26184 flags = kDNSMessageToStringFlags_None;
26185 if( inRawRData ) flags |= kDNSMessageToStringFlag_RawRData;
26186 if( inFormat->altForm == 1 ) flags |= kDNSMessageToStringFlag_MDNS;
26187 if( inFormat->precision == 1 ) flags |= kDNSMessageToStringFlag_OneLine;
26188 err = DNSMessageToString( msgPtr, msgLen, flags, &msgStr );
26189 if( !err )
26190 {
26191 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, msgStr, kSizeCString );
26192 free( msgStr );
26193 }
26194 else
26195 {
26196 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
26197 }
26198
26199 exit:
26200 return( n );
26201 }
26202
26203 //===========================================================================================================================
26204 // _PrintFExtensionHandler_RawDNSMessage
26205 //===========================================================================================================================
26206
26207 static int
26208 _PrintFExtensionHandler_RawDNSMessage(
26209 PrintFContext * inContext,
26210 PrintFFormat * inFormat,
26211 PrintFVAList * inArgs,
26212 void * inUserContext )
26213 {
26214 return( _PrintFExtensionHandler_DNSMessageCommon( inContext, inFormat, inArgs, inUserContext, true ) );
26215 }
26216
26217 //===========================================================================================================================
26218 // _PrintFExtensionHandler_CallbackFlags
26219 //===========================================================================================================================
26220
26221 static int
26222 _PrintFExtensionHandler_CallbackFlags(
26223 PrintFContext * inContext,
26224 PrintFFormat * inFormat,
26225 PrintFVAList * inArgs,
26226 void * inUserContext )
26227 {
26228 DNSServiceFlags flags;
26229 int n;
26230
26231 Unused( inUserContext );
26232
26233 flags = va_arg( inArgs->args, DNSServiceFlags );
26234 require_action_quiet( !inFormat->suppress, exit, n = 0 );
26235
26236 n = PrintFCore( inContext, "%08X %s%c %c%c",
26237 flags, DNSServiceFlagsToAddRmvStr( flags ),
26238 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
26239 ( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
26240 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '*' : ' ' );
26241
26242 exit:
26243 return( n );
26244 }
26245
26246 //===========================================================================================================================
26247 // _PrintFExtensionHandler_DNSRecordData
26248 //===========================================================================================================================
26249
26250 static int
26251 _PrintFExtensionHandler_DNSRecordData(
26252 PrintFContext * inContext,
26253 PrintFFormat * inFormat,
26254 PrintFVAList * inArgs,
26255 void * inUserContext )
26256 {
26257 const void * rdataPtr;
26258 int recordType, n, fieldWidth;
26259 unsigned int rdataLen;
26260
26261 Unused( inUserContext );
26262
26263 recordType = va_arg( inArgs->args, int );
26264 rdataPtr = va_arg( inArgs->args, const void * );
26265 rdataLen = va_arg( inArgs->args, unsigned int );
26266 require_action_quiet( !inFormat->suppress, exit, n = 0 );
26267
26268 check( inFormat->fieldWidth < INT_MAX );
26269 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
26270
26271 if( rdataLen > 0 )
26272 {
26273 char * rdataStr = NULL;
26274
26275 DNSRecordDataToString( rdataPtr, rdataLen, recordType, &rdataStr );
26276 if( rdataStr )
26277 {
26278 n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
26279 free( rdataStr );
26280 }
26281 else
26282 {
26283 n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
26284 }
26285 }
26286 else
26287 {
26288 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
26289 }
26290
26291 exit:
26292 return( n );
26293 }
26294
26295 //===========================================================================================================================
26296 // _PrintFExtensionHandler_DomainName
26297 //===========================================================================================================================
26298
26299 static int
26300 _PrintFExtensionHandler_DomainName(
26301 PrintFContext * inContext,
26302 PrintFFormat * inFormat,
26303 PrintFVAList * inArgs,
26304 void * inUserContext )
26305 {
26306 OSStatus err;
26307 const uint8_t * namePtr;
26308 int n, fieldWidth;
26309 char nameStr[ kDNSServiceMaxDomainName ];
26310
26311 Unused( inUserContext );
26312
26313 namePtr = va_arg( inArgs->args, const uint8_t * );
26314 require_action_quiet( !inFormat->suppress, exit, n = 0 );
26315
26316 check( inFormat->fieldWidth < INT_MAX );
26317 fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
26318
26319 err = DomainNameToString( namePtr, NULL, nameStr, NULL );
26320 check_noerr( err );
26321 if( !err )
26322 {
26323 n = PrintFCore( inContext, "%*s", fieldWidth, nameStr );
26324 }
26325 else
26326 {
26327 n = PrintFCore( inContext, "%*s", fieldWidth, "<< ERROR: domain name conversion failed >>" );
26328 }
26329
26330 exit:
26331 return( n );
26332 }
26333
26334
26335 //===========================================================================================================================
26336 // GetDNSSDFlagsFromOpts
26337 //===========================================================================================================================
26338
26339 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
26340 {
26341 DNSServiceFlags flags;
26342
26343 flags = (DNSServiceFlags) gDNSSDFlags;
26344 if( flags & kDNSServiceFlagsShareConnection )
26345 {
26346 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
26347 kDNSServiceFlagsShareConnection );
26348 }
26349
26350 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
26351 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
26352 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
26353 if( gDNSSDFlag_DenyConstrained ) flags |= kDNSServiceFlagsDenyConstrained;
26354 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
26355 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
26356 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
26357 if( gDNSSDFlag_KnownUnique ) flags |= kDNSServiceFlagsKnownUnique;
26358 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
26359 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
26360 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
26361 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
26362 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
26363 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
26364 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
26365 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
26366 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
26367 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
26368 if( gDNSSDFlag_EnableDNSSEC ) flags |= kDNSServiceFlagsValidate;
26369
26370 return( flags );
26371 }
26372
26373 //===========================================================================================================================
26374 // CreateConnectionFromArgString
26375 //===========================================================================================================================
26376
26377 static OSStatus
26378 CreateConnectionFromArgString(
26379 const char * inString,
26380 dispatch_queue_t inQueue,
26381 DNSServiceRef * outSDRef,
26382 ConnectionDesc * outDesc )
26383 {
26384 OSStatus err;
26385 DNSServiceRef sdRef = NULL;
26386 ConnectionType type;
26387 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
26388 uint8_t uuid[ 16 ];
26389
26390 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
26391 {
26392 err = DNSServiceCreateConnection( &sdRef );
26393 require_noerr( err, exit );
26394 type = kConnectionType_Normal;
26395 }
26396 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
26397 {
26398 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
26399
26400 err = StringToInt32( pidStr, &pid );
26401 if( err )
26402 {
26403 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
26404 err = kParamErr;
26405 goto exit;
26406 }
26407
26408 memset( uuid, 0, sizeof( uuid ) );
26409 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
26410 if( err )
26411 {
26412 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
26413 goto exit;
26414 }
26415 type = kConnectionType_DelegatePID;
26416 }
26417 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
26418 {
26419 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
26420
26421 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
26422
26423 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
26424 if( err )
26425 {
26426 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
26427 err = kParamErr;
26428 goto exit;
26429 }
26430
26431 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
26432 if( err )
26433 {
26434 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
26435 goto exit;
26436 }
26437 type = kConnectionType_DelegateUUID;
26438 }
26439 else
26440 {
26441 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
26442 err = kParamErr;
26443 goto exit;
26444 }
26445
26446 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
26447 require_noerr( err, exit );
26448
26449 *outSDRef = sdRef;
26450 if( outDesc )
26451 {
26452 outDesc->type = type;
26453 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
26454 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
26455 }
26456 sdRef = NULL;
26457
26458 exit:
26459 if( sdRef ) DNSServiceRefDeallocate( sdRef );
26460 return( err );
26461 }
26462
26463 //===========================================================================================================================
26464 // InterfaceIndexFromArgString
26465 //===========================================================================================================================
26466
26467 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
26468 {
26469 OSStatus err;
26470 uint32_t ifIndex;
26471
26472 if( inString )
26473 {
26474 ifIndex = if_nametoindex( inString );
26475 if( ifIndex == 0 )
26476 {
26477 err = StringToUInt32( inString, &ifIndex );
26478 if( err )
26479 {
26480 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
26481 err = kParamErr;
26482 goto exit;
26483 }
26484 }
26485 }
26486 else
26487 {
26488 ifIndex = 0;
26489 }
26490
26491 *outIndex = ifIndex;
26492 err = kNoErr;
26493
26494 exit:
26495 return( err );
26496 }
26497
26498 //===========================================================================================================================
26499 // RecordDataFromArgString
26500 //===========================================================================================================================
26501
26502 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
26503 {
26504 OSStatus err;
26505 uint8_t * dataPtr = NULL;
26506 size_t dataLen;
26507
26508 if( 0 ) {}
26509
26510 // Domain name
26511
26512 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
26513 {
26514 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
26515
26516 err = StringToDomainName( str, &dataPtr, &dataLen );
26517 require_noerr_quiet( err, exit );
26518 }
26519
26520 // File path
26521
26522 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
26523 {
26524 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
26525
26526 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
26527 require_noerr( err, exit );
26528 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26529 }
26530
26531 // Hexadecimal string
26532
26533 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
26534 {
26535 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
26536
26537 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
26538 require_noerr( err, exit );
26539 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26540 }
26541
26542 // IPv4 address string
26543
26544 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
26545 {
26546 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
26547
26548 err = StringToARecordData( str, &dataPtr, &dataLen );
26549 require_noerr_quiet( err, exit );
26550 }
26551
26552 // IPv6 address string
26553
26554 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
26555 {
26556 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
26557
26558 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
26559 require_noerr_quiet( err, exit );
26560 }
26561
26562 // SRV record
26563
26564 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
26565 {
26566 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
26567
26568 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
26569 require_noerr( err, exit );
26570 }
26571
26572 // String with escaped hex and octal bytes
26573
26574 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
26575 {
26576 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
26577 const char * const end = str + strlen( str );
26578 size_t copiedLen;
26579 size_t totalLen;
26580 Boolean success;
26581
26582 if( str < end )
26583 {
26584 success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
26585 require_action( success, exit, err = kParamErr );
26586 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26587
26588 dataLen = totalLen;
26589 dataPtr = (uint8_t *) malloc( dataLen );
26590 require_action( dataPtr, exit, err = kNoMemoryErr );
26591
26592 success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
26593 require_action( success, exit, err = kParamErr );
26594 check( copiedLen == dataLen );
26595 }
26596 else
26597 {
26598 dataPtr = NULL;
26599 dataLen = 0;
26600 }
26601 }
26602
26603 // TXT record
26604
26605 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
26606 {
26607 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
26608
26609 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
26610 require_noerr( err, exit );
26611 }
26612
26613 // Unrecognized format
26614
26615 else
26616 {
26617 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
26618 err = kParamErr;
26619 goto exit;
26620 }
26621
26622 err = kNoErr;
26623 *outDataLen = dataLen;
26624 *outDataPtr = dataPtr;
26625 dataPtr = NULL;
26626
26627 exit:
26628 FreeNullSafe( dataPtr );
26629 return( err );
26630 }
26631
26632 //===========================================================================================================================
26633 // RecordTypeFromArgString
26634 //===========================================================================================================================
26635
26636 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
26637 {
26638 OSStatus err;
26639 uint16_t value;
26640
26641 value = DNSRecordTypeStringToValue( inString );
26642 if( value == 0 )
26643 {
26644 int32_t i32;
26645
26646 err = StringToInt32( inString, &i32 );
26647 require_noerr_quiet( err, exit );
26648 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
26649 value = (uint16_t) i32;
26650 }
26651 *outValue = value;
26652 err = kNoErr;
26653
26654 exit:
26655 return( err );
26656 }
26657
26658 //===========================================================================================================================
26659 // RecordClassFromArgString
26660 //===========================================================================================================================
26661
26662 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
26663 {
26664 OSStatus err;
26665 int32_t i32;
26666
26667 if( strcasecmp( inString, "IN" ) == 0 )
26668 {
26669 *outValue = kDNSServiceClass_IN;
26670 err = kNoErr;
26671 goto exit;
26672 }
26673
26674 err = StringToInt32( inString, &i32 );
26675 require_noerr_quiet( err, exit );
26676 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
26677
26678 *outValue = (uint16_t) i32;
26679
26680 exit:
26681 return( err );
26682 }
26683
26684 //===========================================================================================================================
26685 // InterfaceIndexToName
26686 //===========================================================================================================================
26687
26688 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
26689 {
26690 switch( inIfIndex )
26691 {
26692 case kDNSServiceInterfaceIndexAny:
26693 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
26694 break;
26695
26696 case kDNSServiceInterfaceIndexLocalOnly:
26697 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
26698 break;
26699
26700 case kDNSServiceInterfaceIndexUnicast:
26701 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
26702 break;
26703
26704 case kDNSServiceInterfaceIndexP2P:
26705 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
26706 break;
26707
26708 #if( defined( kDNSServiceInterfaceIndexBLE ) )
26709 case kDNSServiceInterfaceIndexBLE:
26710 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
26711 break;
26712 #endif
26713
26714 default:
26715 {
26716 const char * name;
26717
26718 name = if_indextoname( inIfIndex, inNameBuf );
26719 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
26720 break;
26721 }
26722 }
26723
26724 return( inNameBuf );
26725 }
26726
26727 //===========================================================================================================================
26728 // RecordTypeToString
26729 //===========================================================================================================================
26730
26731 static const char * RecordTypeToString( int inValue )
26732 {
26733 const char * string;
26734
26735 string = DNSRecordTypeValueToString( inValue );
26736 if( !string ) string = "???";
26737 return( string );
26738 }
26739
26740 #if( MDNSRESPONDER_PROJECT )
26741 //===========================================================================================================================
26742 // RecordClassToString
26743 //===========================================================================================================================
26744
26745 static const char * RecordClassToString( int inValue )
26746 {
26747 return( ( inValue == kDNSServiceClass_IN ) ? "IN" : "???" );
26748 }
26749 #endif
26750
26751 //===========================================================================================================================
26752 // WriteDNSQueryMessage
26753 //===========================================================================================================================
26754
26755 static OSStatus
26756 WriteDNSQueryMessage(
26757 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
26758 uint16_t inMsgID,
26759 uint16_t inFlags,
26760 const char * inQName,
26761 uint16_t inQType,
26762 uint16_t inQClass,
26763 size_t * outMsgLen )
26764 {
26765 OSStatus err;
26766 uint8_t qname[ kDomainNameLengthMax ];
26767
26768 err = DomainNameFromString( qname, inQName, NULL );
26769 require_noerr_quiet( err, exit );
26770
26771 err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
26772 require_noerr_quiet( err, exit );
26773
26774 exit:
26775 return( err );
26776 }
26777
26778 //===========================================================================================================================
26779 // DispatchSignalSourceCreate
26780 //===========================================================================================================================
26781
26782 static OSStatus
26783 DispatchSignalSourceCreate(
26784 int inSignal,
26785 dispatch_queue_t inQueue,
26786 DispatchHandler inEventHandler,
26787 void * inContext,
26788 dispatch_source_t * outSource )
26789 {
26790 OSStatus err;
26791 dispatch_source_t source;
26792
26793 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, inQueue );
26794 require_action( source, exit, err = kUnknownErr );
26795
26796 dispatch_set_context( source, inContext );
26797 dispatch_source_set_event_handler_f( source, inEventHandler );
26798
26799 *outSource = source;
26800 err = kNoErr;
26801
26802 exit:
26803 return( err );
26804 }
26805
26806 //===========================================================================================================================
26807 // DispatchSocketSourceCreate
26808 //===========================================================================================================================
26809
26810 static OSStatus
26811 DispatchSocketSourceCreate(
26812 SocketRef inSock,
26813 dispatch_source_type_t inType,
26814 dispatch_queue_t inQueue,
26815 DispatchHandler inEventHandler,
26816 DispatchHandler inCancelHandler,
26817 void * inContext,
26818 dispatch_source_t * outSource )
26819 {
26820 OSStatus err;
26821 dispatch_source_t source;
26822
26823 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
26824 require_action( source, exit, err = kNoResourcesErr );
26825
26826 dispatch_set_context( source, inContext );
26827 dispatch_source_set_event_handler_f( source, inEventHandler );
26828 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
26829
26830 *outSource = source;
26831 err = kNoErr;
26832
26833 exit:
26834 return( err );
26835 }
26836
26837 //===========================================================================================================================
26838 // DispatchTimerCreate
26839 //===========================================================================================================================
26840
26841 static OSStatus
26842 DispatchTimerCreate(
26843 dispatch_time_t inStart,
26844 uint64_t inIntervalNs,
26845 uint64_t inLeewayNs,
26846 dispatch_queue_t inQueue,
26847 DispatchHandler inEventHandler,
26848 DispatchHandler inCancelHandler,
26849 void * inContext,
26850 dispatch_source_t * outTimer )
26851 {
26852 OSStatus err;
26853 dispatch_source_t timer;
26854
26855 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
26856 require_action( timer, exit, err = kNoResourcesErr );
26857
26858 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
26859 dispatch_set_context( timer, inContext );
26860 dispatch_source_set_event_handler_f( timer, inEventHandler );
26861 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
26862
26863 *outTimer = timer;
26864 err = kNoErr;
26865
26866 exit:
26867 return( err );
26868 }
26869
26870 #if( TARGET_OS_DARWIN )
26871 //===========================================================================================================================
26872 // DispatchProcessMonitorCreate
26873 //===========================================================================================================================
26874
26875 static OSStatus
26876 DispatchProcessMonitorCreate(
26877 pid_t inPID,
26878 unsigned long inFlags,
26879 dispatch_queue_t inQueue,
26880 DispatchHandler inEventHandler,
26881 DispatchHandler inCancelHandler,
26882 void * inContext,
26883 dispatch_source_t * outMonitor )
26884 {
26885 OSStatus err;
26886 dispatch_source_t monitor;
26887
26888 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
26889 inQueue ? inQueue : dispatch_get_main_queue() );
26890 require_action( monitor, exit, err = kUnknownErr );
26891
26892 dispatch_set_context( monitor, inContext );
26893 dispatch_source_set_event_handler_f( monitor, inEventHandler );
26894 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
26895
26896 *outMonitor = monitor;
26897 err = kNoErr;
26898
26899 exit:
26900 return( err );
26901 }
26902 #endif
26903
26904 //===========================================================================================================================
26905 // ServiceTypeDescription
26906 //===========================================================================================================================
26907
26908 typedef struct
26909 {
26910 const char * name; // Name of the service type in two-label "_service._proto" format.
26911 const char * description; // Description of the service type.
26912
26913 } ServiceType;
26914
26915 // A Non-comprehensive table of DNS-SD service types
26916
26917 static const ServiceType kServiceTypes[] =
26918 {
26919 { "_acp-sync._tcp", "AirPort Base Station Sync" },
26920 { "_adisk._tcp", "Automatic Disk Discovery" },
26921 { "_afpovertcp._tcp", "Apple File Sharing" },
26922 { "_airdrop._tcp", "AirDrop" },
26923 { "_airplay._tcp", "AirPlay" },
26924 { "_airport._tcp", "AirPort Base Station" },
26925 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
26926 { "_eppc._tcp", "Remote AppleEvents" },
26927 { "_ftp._tcp", "File Transfer Protocol" },
26928 { "_home-sharing._tcp", "Home Sharing" },
26929 { "_homekit._tcp", "HomeKit" },
26930 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
26931 { "_https._tcp", "HTTP over SSL/TLS" },
26932 { "_ipp._tcp", "Internet Printing Protocol" },
26933 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
26934 { "_mediaremotetv._tcp", "Media Remote" },
26935 { "_net-assistant._tcp", "Apple Remote Desktop" },
26936 { "_od-master._tcp", "OpenDirectory Master" },
26937 { "_nfs._tcp", "Network File System" },
26938 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
26939 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
26940 { "_raop._tcp", "Remote Audio Output Protocol" },
26941 { "_rfb._tcp", "Remote Frame Buffer" },
26942 { "_scanner._tcp", "Bonjour Scanning" },
26943 { "_smb._tcp", "Server Message Block over TCP/IP" },
26944 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
26945 { "_sleep-proxy._udp", "Sleep Proxy Server" },
26946 { "_ssh._tcp", "SSH Remote Login Protocol" },
26947 { "_teleport._tcp", "teleport" },
26948 { "_tftp._tcp", "Trivial File Transfer Protocol" },
26949 { "_workstation._tcp", "Workgroup Manager" },
26950 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
26951 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
26952 };
26953
26954 static const char * ServiceTypeDescription( const char *inName )
26955 {
26956 const ServiceType * serviceType;
26957 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
26958
26959 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
26960 {
26961 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
26962 {
26963 const char * const ptr = &inName[ strlen( serviceType->name ) ];
26964
26965 if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
26966 {
26967 return( serviceType->description );
26968 }
26969 }
26970 }
26971 return( NULL );
26972 }
26973
26974 //===========================================================================================================================
26975 // SocketContextCreate
26976 //===========================================================================================================================
26977
26978 static SocketContext * SocketContextCreate( SocketRef inSock, void *inUserContext, OSStatus *outError )
26979 {
26980 return( SocketContextCreateEx( inSock, inUserContext, NULL, outError ) );
26981 }
26982
26983 //===========================================================================================================================
26984 // SocketContextCreateEx
26985 //===========================================================================================================================
26986
26987 static SocketContext *
26988 SocketContextCreateEx(
26989 SocketRef inSock,
26990 void * inUserContext,
26991 SocketContextFinalizer_f inUserFinalizer,
26992 OSStatus * outError )
26993 {
26994 OSStatus err;
26995 SocketContext * context;
26996
26997 context = (SocketContext *) calloc( 1, sizeof( *context ) );
26998 require_action( context, exit, err = kNoMemoryErr );
26999
27000 context->refCount = 1;
27001 context->sock = inSock;
27002 context->userContext = inUserContext;
27003 context->userFinalizer = inUserFinalizer;
27004 err = kNoErr;
27005
27006 exit:
27007 if( outError ) *outError = err;
27008 return( context );
27009 }
27010
27011 //===========================================================================================================================
27012 // SocketContextRetain
27013 //===========================================================================================================================
27014
27015 static SocketContext * SocketContextRetain( SocketContext *inContext )
27016 {
27017 atomic_add_32( &inContext->refCount, 1 );
27018 return( inContext );
27019 }
27020
27021 //===========================================================================================================================
27022 // SocketContextRelease
27023 //===========================================================================================================================
27024
27025 static void SocketContextRelease( SocketContext *me )
27026 {
27027 if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
27028 {
27029 ForgetSocket( &me->sock );
27030 if( me->userFinalizer )
27031 {
27032 me->userFinalizer( me->userContext );
27033 me->userFinalizer = NULL;
27034 }
27035 me->userContext = NULL;
27036 free( me );
27037 }
27038 }
27039
27040 //===========================================================================================================================
27041 // SocketContextCancelHandler
27042 //===========================================================================================================================
27043
27044 static void SocketContextCancelHandler( void *inContext )
27045 {
27046 SocketContextRelease( (SocketContext *) inContext );
27047 }
27048
27049 //===========================================================================================================================
27050 // SocketContextFinalizerCF
27051 //===========================================================================================================================
27052
27053 static void SocketContextFinalizerCF( void *inUserCtx )
27054 {
27055 CFRelease( (CFTypeRef) inUserCtx );
27056 }
27057
27058 //===========================================================================================================================
27059 // StringToInt32
27060 //===========================================================================================================================
27061
27062 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
27063 {
27064 OSStatus err;
27065 long value;
27066 char * endPtr;
27067
27068 value = strtol( inString, &endPtr, 0 );
27069 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
27070 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
27071
27072 *outValue = (int32_t) value;
27073 err = kNoErr;
27074
27075 exit:
27076 return( err );
27077 }
27078
27079 //===========================================================================================================================
27080 // StringToUInt32
27081 //===========================================================================================================================
27082
27083 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
27084 {
27085 OSStatus err;
27086 uint32_t value;
27087 char * endPtr;
27088
27089 value = (uint32_t) strtol( inString, &endPtr, 0 );
27090 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
27091
27092 *outValue = value;
27093 err = kNoErr;
27094
27095 exit:
27096 return( err );
27097 }
27098
27099 #if( TARGET_OS_DARWIN )
27100 //===========================================================================================================================
27101 // _StringToInt64
27102 //===========================================================================================================================
27103
27104 static int64_t _StringToInt64( const char *inString, OSStatus *outError )
27105 {
27106 OSStatus err;
27107 long long ll;
27108 char * end;
27109 int64_t i64 = 0;
27110 int errnoVal;
27111
27112 set_errno_compat( 0 );
27113 ll = strtoll( inString, &end, 0 );
27114 errnoVal = errno_compat();
27115 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
27116 require_action_quiet( ( ( ll != LLONG_MIN ) && ( ll != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
27117 require_action_quiet( ( ll >= INT64_MIN ) && ( ll <= INT64_MAX ), exit, err = kRangeErr );
27118 i64 = (int64_t) ll;
27119 err = kNoErr;
27120
27121 exit:
27122 if( outError ) *outError = err;
27123 return( i64 );
27124 }
27125
27126 //===========================================================================================================================
27127 // _StringToUInt64
27128 //===========================================================================================================================
27129
27130 static uint64_t _StringToUInt64( const char *inString, OSStatus *outError )
27131 {
27132 OSStatus err;
27133 unsigned long long val;
27134 char * end;
27135 int errnoVal;
27136
27137 set_errno_compat( 0 );
27138 val = strtoull( inString, &end, 0 );
27139 errnoVal = errno_compat();
27140
27141 require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
27142 require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
27143 require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
27144 err = kNoErr;
27145
27146 exit:
27147 if( outError ) *outError = err;
27148 return( (uint64_t)val );
27149 }
27150
27151 //===========================================================================================================================
27152 // _StringToPID
27153 //===========================================================================================================================
27154
27155 static pid_t _StringToPID( const char *inString, OSStatus *outError )
27156 {
27157 OSStatus err;
27158 int64_t i64;
27159 pid_t pid = 0;
27160
27161 i64 = _StringToInt64( inString, &err );
27162 require_noerr_quiet( err, exit );
27163 require_action_quiet( i64 == (pid_t) i64, exit, err = kRangeErr );
27164 pid = (pid_t) i64;
27165 err = kNoErr;
27166
27167 exit:
27168 if( outError ) *outError = err;
27169 return( pid );
27170 }
27171
27172 //===========================================================================================================================
27173 // _ParseEscapedString
27174 //
27175 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
27176 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
27177 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
27178 //===========================================================================================================================
27179
27180 static OSStatus
27181 _ParseEscapedString(
27182 const char * inSrc,
27183 const char * inEnd,
27184 const char * inDelimiters,
27185 char * inBufPtr,
27186 size_t inBufLen,
27187 size_t * outCopiedLen,
27188 size_t * outActualLen,
27189 const char ** outPtr )
27190 {
27191 OSStatus err;
27192 const char * ptr;
27193 char * dst = inBufPtr;
27194 const char * const lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
27195 size_t len;
27196
27197 len = 0;
27198 ptr = inSrc;
27199 if( !inDelimiters ) inDelimiters = "";
27200 while( ptr < inEnd )
27201 {
27202 int c;
27203 const char * del;
27204
27205 c = *ptr;
27206 for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
27207 if( *del != '\0' ) break;
27208 ++ptr;
27209 if( c == '\\' )
27210 {
27211 require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
27212 c = *ptr++;
27213 }
27214 ++len;
27215 if( dst < lim ) *dst++ = (char) c;
27216 }
27217 if( inBufLen > 0 ) *dst = '\0';
27218
27219 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBufPtr );
27220 if( outActualLen ) *outActualLen = len;
27221 if( outPtr ) *outPtr = ptr;
27222 err = kNoErr;
27223
27224 exit:
27225 return( err );
27226 }
27227 #endif
27228
27229 //===========================================================================================================================
27230 // StringToARecordData
27231 //===========================================================================================================================
27232
27233 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
27234 {
27235 OSStatus err;
27236 uint32_t * addrPtr;
27237 const size_t addrLen = sizeof( *addrPtr );
27238 const char * end;
27239
27240 addrPtr = (uint32_t *) malloc( addrLen );
27241 require_action( addrPtr, exit, err = kNoMemoryErr );
27242
27243 err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
27244 NULL, NULL, NULL, &end );
27245 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
27246 require_noerr_quiet( err, exit );
27247
27248 *addrPtr = HostToBig32( *addrPtr );
27249
27250 *outPtr = (uint8_t *) addrPtr;
27251 addrPtr = NULL;
27252 *outLen = addrLen;
27253
27254 exit:
27255 FreeNullSafe( addrPtr );
27256 return( err );
27257 }
27258
27259 //===========================================================================================================================
27260 // StringToAAAARecordData
27261 //===========================================================================================================================
27262
27263 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
27264 {
27265 OSStatus err;
27266 uint8_t * addrPtr;
27267 const size_t addrLen = 16;
27268 const char * end;
27269
27270 addrPtr = (uint8_t *) malloc( addrLen );
27271 require_action( addrPtr, exit, err = kNoMemoryErr );
27272
27273 err = _StringToIPv6Address( inString,
27274 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
27275 addrPtr, NULL, NULL, NULL, &end );
27276 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
27277 require_noerr_quiet( err, exit );
27278
27279 *outPtr = addrPtr;
27280 addrPtr = NULL;
27281 *outLen = addrLen;
27282
27283 exit:
27284 FreeNullSafe( addrPtr );
27285 return( err );
27286 }
27287
27288 //===========================================================================================================================
27289 // StringToDomainName
27290 //===========================================================================================================================
27291
27292 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
27293 {
27294 OSStatus err;
27295 uint8_t * namePtr;
27296 size_t nameLen;
27297 uint8_t * end;
27298 uint8_t nameBuf[ kDomainNameLengthMax ];
27299
27300 err = DomainNameFromString( nameBuf, inString, &end );
27301 require_noerr_quiet( err, exit );
27302
27303 nameLen = (size_t)( end - nameBuf );
27304 namePtr = _memdup( nameBuf, nameLen );
27305 require_action( namePtr, exit, err = kNoMemoryErr );
27306
27307 *outPtr = namePtr;
27308 namePtr = NULL;
27309 if( outLen ) *outLen = nameLen;
27310
27311 exit:
27312 return( err );
27313 }
27314
27315 #if( TARGET_OS_DARWIN )
27316 //===========================================================================================================================
27317 // GetDefaultDNSServer
27318 //===========================================================================================================================
27319
27320 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
27321 {
27322 OSStatus err;
27323 dns_config_t * config;
27324 struct sockaddr * addr;
27325 int32_t i;
27326
27327 config = dns_configuration_copy();
27328 require_action( config, exit, err = kUnknownErr );
27329
27330 addr = NULL;
27331 for( i = 0; i < config->n_resolver; ++i )
27332 {
27333 const dns_resolver_t * const resolver = config->resolver[ i ];
27334
27335 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
27336 {
27337 addr = resolver->nameserver[ 0 ];
27338 break;
27339 }
27340 }
27341 require_action_quiet( addr, exit, err = kNotFoundErr );
27342
27343 SockAddrCopy( addr, outAddr );
27344 err = kNoErr;
27345
27346 exit:
27347 if( config ) dns_configuration_free( config );
27348 return( err );
27349 }
27350 #endif
27351
27352 //===========================================================================================================================
27353 // GetMDNSMulticastAddrV4
27354 //===========================================================================================================================
27355
27356 static void _MDNSMulticastAddrV4Init( void *inContext );
27357
27358 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
27359 {
27360 static struct sockaddr_in sMDNSMulticastAddrV4;
27361 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
27362
27363 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
27364 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
27365 }
27366
27367 static void _MDNSMulticastAddrV4Init( void *inContext )
27368 {
27369 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
27370
27371 _SockAddrInitIPv4( addr, UINT32_C( 0xE00000FB ), kMDNSPort ); // The mDNS IPv4 multicast address is 224.0.0.251.
27372 }
27373
27374 //===========================================================================================================================
27375 // GetMDNSMulticastAddrV6
27376 //===========================================================================================================================
27377
27378 static void _MDNSMulticastAddrV6Init( void *inContext );
27379
27380 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
27381 {
27382 static struct sockaddr_in6 sMDNSMulticastAddrV6;
27383 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
27384
27385 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
27386 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
27387 }
27388
27389 static void _MDNSMulticastAddrV6Init( void *inContext )
27390 {
27391 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
27392
27393 memset( addr, 0, sizeof( *addr ) );
27394 SIN6_LEN_SET( addr );
27395 addr->sin6_family = AF_INET6;
27396 addr->sin6_port = htons( kMDNSPort );
27397 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
27398 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
27399 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
27400 }
27401
27402 //===========================================================================================================================
27403 // CreateMulticastSocket
27404 //===========================================================================================================================
27405
27406 static OSStatus
27407 CreateMulticastSocket(
27408 const struct sockaddr * inAddr,
27409 int inPort,
27410 const char * inIfName,
27411 uint32_t inIfIndex,
27412 Boolean inJoin,
27413 int * outPort,
27414 SocketRef * outSock )
27415 {
27416 OSStatus err;
27417 SocketRef sock = kInvalidSocketRef;
27418 const int family = inAddr->sa_family;
27419 int port;
27420
27421 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
27422
27423 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
27424 require_noerr_quiet( err, exit );
27425
27426 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
27427 require_noerr_quiet( err, exit );
27428
27429 if( family == AF_INET )
27430 {
27431 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
27432 err = map_socket_noerr_errno( sock, err );
27433 require_noerr_quiet( err, exit );
27434 }
27435 else
27436 {
27437 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
27438 err = map_socket_noerr_errno( sock, err );
27439 require_noerr_quiet( err, exit );
27440 }
27441
27442 if( inJoin )
27443 {
27444 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
27445 require_noerr_quiet( err, exit );
27446 }
27447
27448 if( outPort ) *outPort = port;
27449 *outSock = sock;
27450 sock = kInvalidSocketRef;
27451
27452 exit:
27453 ForgetSocket( &sock );
27454 return( err );
27455 }
27456
27457 //===========================================================================================================================
27458 // DecimalTextToUInt32
27459 //===========================================================================================================================
27460
27461 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
27462 {
27463 OSStatus err;
27464 uint64_t value;
27465 const char * ptr = inSrc;
27466
27467 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
27468
27469 value = (uint64_t)( *ptr++ - '0' );
27470 if( value == 0 )
27471 {
27472 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
27473 {
27474 err = kMalformedErr;
27475 goto exit;
27476 }
27477 }
27478 else
27479 {
27480 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
27481 {
27482 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
27483 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
27484 }
27485 }
27486
27487 *outValue = (uint32_t) value;
27488 if( outPtr ) *outPtr = ptr;
27489 err = kNoErr;
27490
27491 exit:
27492 return( err );
27493 }
27494
27495 //===========================================================================================================================
27496 // CheckIntegerArgument
27497 //===========================================================================================================================
27498
27499 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
27500 {
27501 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
27502
27503 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
27504 return( kRangeErr );
27505 }
27506
27507 //===========================================================================================================================
27508 // CheckDoubleArgument
27509 //===========================================================================================================================
27510
27511 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
27512 {
27513 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
27514
27515 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
27516 return( kRangeErr );
27517 }
27518
27519 //===========================================================================================================================
27520 // CheckRootUser
27521 //===========================================================================================================================
27522
27523 static OSStatus CheckRootUser( void )
27524 {
27525 if( geteuid() == 0 ) return( kNoErr );
27526
27527 FPrintF( stderr, "error: This command must to be run as root.\n" );
27528 return( kPermissionErr );
27529 }
27530
27531 //===========================================================================================================================
27532 // _SpawnCommand
27533 //===========================================================================================================================
27534
27535 extern char ** environ;
27536
27537 static OSStatus
27538 _SpawnCommand(
27539 pid_t * outPID,
27540 const char * inStdOutRedirect,
27541 const char * inStdErrRedirect,
27542 const char * inFormat,
27543 ... )
27544 {
27545 OSStatus err;
27546 va_list args;
27547 char * cmdStr = NULL;
27548 size_t cmdLen;
27549 const char * cmdEnd;
27550 char * bufPtr = NULL;
27551 size_t bufLen;
27552 const char * bufLim;
27553 pid_t pid;
27554 const char * src;
27555 char * dst;
27556 char ** argArray = NULL;
27557 size_t argCapacity, argCount;
27558 posix_spawn_file_actions_t actions;
27559 posix_spawn_file_actions_t * actionsPtr = NULL;
27560
27561 // Create command string from format string and its arguments.
27562
27563 va_start( args, inFormat );
27564 VASPrintF( &cmdStr, inFormat, args );
27565 va_end( args );
27566 require_action( cmdStr, exit, err = kUnknownErr );
27567
27568 cmdLen = strlen( cmdStr );
27569 cmdEnd = &cmdStr[ cmdLen ];
27570
27571 // Allocate buffer for argument strings.
27572 // In the worst-case scenario in terms of memory requirements, the only non-escaped white space is one non-escaped
27573 // white space character between each pair of adjacent arguments. The amount of required memory is equal to the size
27574 // of cmdStr.
27575
27576 bufLen = cmdLen + 1; // +1 for NUL terminator.
27577 bufPtr = (char *) malloc( bufLen );
27578 require_action( bufPtr, exit, err = kNoMemoryErr );
27579 bufLim = &bufPtr[ bufLen ];
27580
27581 // Allocate initial argument array.
27582
27583 argCount = 0;
27584 argCapacity = 8;
27585 argArray = (char **) malloc( ( argCapacity + 1 ) * sizeof( *argArray ) ); // +1 for NULL arg.
27586 require_action( argArray, exit, err = kNoMemoryErr );
27587
27588 // Extract argument strings from command string.
27589
27590 src = cmdStr;
27591 dst = bufPtr;
27592 for( ;; )
27593 {
27594 size_t maxLen, copiedLen, totalLen;
27595 Boolean more;
27596
27597 maxLen = (size_t)( bufLim - dst );
27598 if( maxLen > 0 ) --maxLen; // -1 for NUL terminator.
27599 more = _ParseQuotedEscapedString( src, cmdEnd, kWhiteSpaceCharSet, dst, maxLen, &copiedLen, &totalLen, &src );
27600 if( !more ) break;
27601 require_fatal( copiedLen == totalLen, "Incorrect assumption about maximum required buffer space." );
27602
27603 if( argCount >= argCapacity )
27604 {
27605 size_t newCapactiy;
27606 char ** newArray;
27607
27608 newCapactiy = 2 * argCapacity;
27609 newArray = (char **) realloc( argArray, ( newCapactiy + 1 ) * sizeof( *newArray ) ); // +1 for NULL arg.
27610 require_action( newArray, exit, err = kNoMemoryErr );
27611
27612 argArray = newArray;
27613 argCapacity = newCapactiy;
27614 }
27615 argArray[ argCount++ ] = dst;
27616 dst += copiedLen;
27617 *dst++ = '\0';
27618 check( dst <= bufLim );
27619 }
27620 require_action_quiet( argCount > 0, exit, err = kCommandErr );
27621
27622 argArray[ argCount ] = NULL;
27623
27624 // Set up stdout and stderr redirections, if any.
27625
27626 if( inStdOutRedirect || inStdErrRedirect )
27627 {
27628 err = posix_spawn_file_actions_init( &actions );
27629 require_noerr( err, exit );
27630
27631 actionsPtr = &actions;
27632 if( inStdOutRedirect )
27633 {
27634 err = posix_spawn_file_actions_addopen( actionsPtr, STDOUT_FILENO, inStdOutRedirect, O_WRONLY, 0 );
27635 require_noerr( err, exit );
27636 }
27637 if( inStdErrRedirect )
27638 {
27639 err = posix_spawn_file_actions_addopen( actionsPtr, STDERR_FILENO, inStdErrRedirect, O_WRONLY, 0 );
27640 require_noerr( err, exit );
27641 }
27642 }
27643 // Spawn command.
27644
27645 err = posix_spawnp( &pid, argArray[ 0 ], actionsPtr, NULL, argArray, environ );
27646 require_noerr_quiet( err, exit );
27647
27648 if( outPID ) *outPID = pid;
27649
27650 exit:
27651 FreeNullSafe( cmdStr );
27652 FreeNullSafe( bufPtr );
27653 FreeNullSafe( argArray );
27654 if( actionsPtr ) posix_spawn_file_actions_destroy( actionsPtr );
27655 return( err );
27656 }
27657
27658 //===========================================================================================================================
27659 // OutputFormatFromArgString
27660 //===========================================================================================================================
27661
27662 static OSStatus OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
27663 {
27664 OSStatus err;
27665 OutputFormatType format;
27666
27667 format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
27668 kOutputFormatStr_JSON, kOutputFormatType_JSON,
27669 kOutputFormatStr_XML, kOutputFormatType_XML,
27670 kOutputFormatStr_Binary, kOutputFormatType_Binary,
27671 NULL );
27672 if( outFormat ) *outFormat = format;
27673 return( err );
27674 }
27675
27676 //===========================================================================================================================
27677 // OutputPropertyList
27678 //===========================================================================================================================
27679
27680 static OSStatus OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
27681 {
27682 OSStatus err;
27683 CFDataRef results = NULL;
27684 FILE * file = NULL;
27685
27686 // Convert plist to a specific format.
27687
27688 switch( inType )
27689 {
27690 case kOutputFormatType_JSON:
27691 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
27692 require_action( results, exit, err = kUnknownErr );
27693 break;
27694
27695 case kOutputFormatType_XML:
27696 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
27697 require_action( results, exit, err = kUnknownErr );
27698 break;
27699
27700 case kOutputFormatType_Binary:
27701 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
27702 require_action( results, exit, err = kUnknownErr );
27703 break;
27704
27705 default:
27706 err = kTypeErr;
27707 goto exit;
27708 }
27709
27710 // Write formatted results to file or stdout.
27711
27712 if( inOutputFilePath )
27713 {
27714 file = fopen( inOutputFilePath, "wb" );
27715 err = map_global_value_errno( file, file );
27716 require_noerr( err, exit );
27717 }
27718 else
27719 {
27720 file = stdout;
27721 }
27722
27723 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
27724 require_noerr_quiet( err, exit );
27725
27726 // Write a trailing newline for JSON-formatted results.
27727
27728 if( inType == kOutputFormatType_JSON )
27729 {
27730 err = WriteANSIFile( file, "\n", 1 );
27731 require_noerr_quiet( err, exit );
27732 }
27733
27734 exit:
27735 if( file && ( file != stdout ) ) fclose( file );
27736 CFReleaseNullSafe( results );
27737 return( err );
27738 }
27739
27740 //===========================================================================================================================
27741 // CreateSRVRecordDataFromString
27742 //===========================================================================================================================
27743
27744 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
27745 {
27746 OSStatus err;
27747 DataBuffer dataBuf;
27748 const char * ptr;
27749 int i;
27750 uint8_t * end;
27751 uint8_t target[ kDomainNameLengthMax ];
27752
27753 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
27754
27755 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
27756
27757 ptr = inString;
27758 for( i = 0; i < 3; ++i )
27759 {
27760 char * next;
27761 long value;
27762 uint8_t buf[ 2 ];
27763
27764 value = strtol( ptr, &next, 0 );
27765 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
27766 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
27767 ptr = next + 1;
27768
27769 WriteBig16( buf, value );
27770
27771 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
27772 require_noerr( err, exit );
27773 }
27774
27775 // Set the target domain name.
27776
27777 err = DomainNameFromString( target, ptr, &end );
27778 require_noerr_quiet( err, exit );
27779
27780 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
27781 require_noerr( err, exit );
27782
27783 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
27784 require_noerr( err, exit );
27785
27786 exit:
27787 DataBuffer_Free( &dataBuf );
27788 return( err );
27789 }
27790
27791 //===========================================================================================================================
27792 // CreateTXTRecordDataFromString
27793 //===========================================================================================================================
27794
27795 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
27796 {
27797 OSStatus err;
27798 DataBuffer dataBuf;
27799 const char * src;
27800 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
27801
27802 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
27803
27804 src = inString;
27805 for( ;; )
27806 {
27807 uint8_t * dst = &txtStr[ 1 ];
27808 const uint8_t * const lim = &txtStr[ 256 ];
27809 int c;
27810
27811 while( *src && ( *src != inDelimiter ) )
27812 {
27813 if( ( c = *src++ ) == '\\' )
27814 {
27815 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
27816 c = *src++;
27817 }
27818 require_action_quiet( dst < lim, exit, err = kOverrunErr );
27819 *dst++ = (uint8_t) c;
27820 }
27821 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
27822 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
27823 require_noerr( err, exit );
27824
27825 if( *src == '\0' ) break;
27826 ++src;
27827 }
27828
27829 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
27830 require_noerr( err, exit );
27831
27832 exit:
27833 DataBuffer_Free( &dataBuf );
27834 return( err );
27835 }
27836
27837 //===========================================================================================================================
27838 // CreateNSECRecordData
27839 //===========================================================================================================================
27840
27841 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
27842 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
27843
27844 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
27845
27846 static OSStatus
27847 CreateNSECRecordData(
27848 const uint8_t * inNextDomainName,
27849 uint8_t ** outPtr,
27850 size_t * outLen,
27851 unsigned int inTypeCount,
27852 ... )
27853 {
27854 OSStatus err;
27855 va_list args;
27856 DataBuffer rdataDB;
27857 unsigned int * array = NULL;
27858 unsigned int i, type, maxBit, currBlock, bitmapLen;
27859 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
27860 uint8_t * const bitmap = &fields[ 2 ];
27861
27862 va_start( args, inTypeCount );
27863 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
27864
27865 // Append Next Domain Name.
27866
27867 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
27868 require_noerr( err, exit );
27869
27870 // Append Type Bit Maps.
27871
27872 maxBit = 0;
27873 memset( bitmap, 0, kNSECBitmapMaxLength );
27874 if( inTypeCount > 0 )
27875 {
27876 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
27877 require_action( array, exit, err = kNoMemoryErr );
27878
27879 for( i = 0; i < inTypeCount; ++i )
27880 {
27881 type = va_arg( args, unsigned int );
27882 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
27883 array[ i ] = type;
27884 }
27885 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
27886
27887 currBlock = array[ 0 ] / 256;
27888 for( i = 0; i < inTypeCount; ++i )
27889 {
27890 const unsigned int block = array[ i ] / 256;
27891 const unsigned int bit = array[ i ] % 256;
27892
27893 if( block != currBlock )
27894 {
27895 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
27896 fields[ 0 ] = (uint8_t) currBlock;
27897 fields[ 1 ] = (uint8_t) bitmapLen;
27898
27899 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
27900 require_noerr( err, exit );
27901
27902 maxBit = 0;
27903 currBlock = block;
27904 memset( bitmap, 0, bitmapLen );
27905 }
27906 BitArray_SetBit( bitmap, bit );
27907 if( bit > maxBit ) maxBit = bit;
27908 }
27909 }
27910 else
27911 {
27912 currBlock = 0;
27913 }
27914
27915 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
27916 fields[ 0 ] = (uint8_t) currBlock;
27917 fields[ 1 ] = (uint8_t) bitmapLen;
27918
27919 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
27920 require_noerr( err, exit );
27921
27922 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
27923 require_noerr( err, exit );
27924
27925 exit:
27926 va_end( args );
27927 DataBuffer_Free( &rdataDB );
27928 FreeNullSafe( array );
27929 return( err );
27930 }
27931
27932 //===========================================================================================================================
27933 // AppendSOARecord
27934 //===========================================================================================================================
27935
27936 static OSStatus
27937 _AppendSOARecordData(
27938 DataBuffer * inDB,
27939 const uint8_t * inMName,
27940 const uint8_t * inRName,
27941 uint32_t inSerial,
27942 uint32_t inRefresh,
27943 uint32_t inRetry,
27944 uint32_t inExpire,
27945 uint32_t inMinimumTTL,
27946 size_t * outLen );
27947
27948 static OSStatus
27949 AppendSOARecord(
27950 DataBuffer * inDB,
27951 const uint8_t * inNamePtr,
27952 size_t inNameLen,
27953 uint16_t inType,
27954 uint16_t inClass,
27955 uint32_t inTTL,
27956 const uint8_t * inMName,
27957 const uint8_t * inRName,
27958 uint32_t inSerial,
27959 uint32_t inRefresh,
27960 uint32_t inRetry,
27961 uint32_t inExpire,
27962 uint32_t inMinimumTTL,
27963 size_t * outLen )
27964 {
27965 OSStatus err;
27966 size_t rdataLen;
27967 size_t rdlengthOffset = 0;
27968 uint8_t * rdlengthPtr;
27969
27970 if( inDB )
27971 {
27972 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
27973 require_noerr( err, exit );
27974
27975 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
27976 }
27977
27978 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
27979 require_noerr( err, exit );
27980
27981 if( inDB )
27982 {
27983 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
27984 WriteBig16( rdlengthPtr, rdataLen );
27985 }
27986
27987 if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
27988 err = kNoErr;
27989
27990 exit:
27991 return( err );
27992 }
27993
27994 static OSStatus
27995 _AppendSOARecordData(
27996 DataBuffer * inDB,
27997 const uint8_t * inMName,
27998 const uint8_t * inRName,
27999 uint32_t inSerial,
28000 uint32_t inRefresh,
28001 uint32_t inRetry,
28002 uint32_t inExpire,
28003 uint32_t inMinimumTTL,
28004 size_t * outLen )
28005 {
28006 OSStatus err;
28007 dns_fixed_fields_soa fields;
28008 const size_t mnameLen = DomainNameLength( inMName );
28009 const size_t rnameLen = DomainNameLength( inRName );
28010
28011 if( inDB )
28012 {
28013 err = DataBuffer_Append( inDB, inMName, mnameLen );
28014 require_noerr( err, exit );
28015
28016 err = DataBuffer_Append( inDB, inRName, rnameLen );
28017 require_noerr( err, exit );
28018
28019 dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
28020 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28021 require_noerr( err, exit );
28022 }
28023 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
28024 err = kNoErr;
28025
28026 exit:
28027 return( err );
28028 }
28029
28030 //===========================================================================================================================
28031 // CreateSOARecordData
28032 //===========================================================================================================================
28033
28034 static OSStatus
28035 CreateSOARecordData(
28036 const uint8_t * inMName,
28037 const uint8_t * inRName,
28038 uint32_t inSerial,
28039 uint32_t inRefresh,
28040 uint32_t inRetry,
28041 uint32_t inExpire,
28042 uint32_t inMinimumTTL,
28043 uint8_t ** outPtr,
28044 size_t * outLen )
28045 {
28046 OSStatus err;
28047 DataBuffer rdataDB;
28048
28049 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
28050
28051 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
28052 require_noerr( err, exit );
28053
28054 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
28055 require_noerr( err, exit );
28056
28057 exit:
28058 DataBuffer_Free( &rdataDB );
28059 return( err );
28060 }
28061
28062 //===========================================================================================================================
28063 // _DataBuffer_AppendDNSQuestion
28064 //===========================================================================================================================
28065
28066 static OSStatus
28067 _DataBuffer_AppendDNSQuestion(
28068 DataBuffer * inDB,
28069 const uint8_t * inNamePtr,
28070 size_t inNameLen,
28071 uint16_t inType,
28072 uint16_t inClass )
28073 {
28074 OSStatus err;
28075 dns_fixed_fields_question fields;
28076
28077 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
28078 require_noerr( err, exit );
28079
28080 dns_fixed_fields_question_init( &fields, inType, inClass );
28081 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28082 require_noerr( err, exit );
28083
28084 exit:
28085 return( err );
28086 }
28087
28088 //===========================================================================================================================
28089 // _DataBuffer_AppendDNSRecord
28090 //===========================================================================================================================
28091
28092 static OSStatus
28093 _DataBuffer_AppendDNSRecord(
28094 DataBuffer * inDB,
28095 const uint8_t * inNamePtr,
28096 size_t inNameLen,
28097 uint16_t inType,
28098 uint16_t inClass,
28099 uint32_t inTTL,
28100 const uint8_t * inRDataPtr,
28101 size_t inRDataLen )
28102 {
28103 OSStatus err;
28104 dns_fixed_fields_record fields;
28105
28106 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
28107
28108 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
28109 require_noerr( err, exit );
28110
28111 dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
28112 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28113 require_noerr( err, exit );
28114
28115 if( inRDataPtr )
28116 {
28117 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
28118 require_noerr( err, exit );
28119 }
28120
28121 exit:
28122 return( err );
28123 }
28124
28125 //===========================================================================================================================
28126 // _NanoTime64ToTimestamp
28127 //===========================================================================================================================
28128
28129 static char * _NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
28130 {
28131 struct timeval tv;
28132
28133 NanoTimeToTimeVal( inTime, &tv );
28134 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
28135 }
28136
28137 //===========================================================================================================================
28138 // _MDNSInterfaceListCreate
28139 //===========================================================================================================================
28140
28141 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
28142
28143 static OSStatus _MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
28144 {
28145 OSStatus err;
28146 struct ifaddrs * ifaList;
28147 const struct ifaddrs * ifa;
28148 MDNSInterfaceItem * interfaceList;
28149 MDNSInterfaceItem ** ptr;
28150 SocketRef infoSock;
28151
28152 ifaList = NULL;
28153 interfaceList = NULL;
28154 infoSock = kInvalidSocketRef;
28155 if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
28156 require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
28157
28158 infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
28159 err = map_socket_creation_errno( infoSock );
28160 require_noerr( err, exit );
28161
28162 err = getifaddrs( &ifaList );
28163 err = map_global_noerr_errno( err );
28164 require_noerr( err, exit );
28165
28166 ptr = &interfaceList;
28167 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
28168 {
28169 MDNSInterfaceItem * item;
28170 int family;
28171 const unsigned int flagsMask = IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
28172 const unsigned int flagsNeeded = IFF_UP | IFF_MULTICAST;
28173
28174 if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded ) continue;
28175 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
28176 family = ifa->ifa_addr->sa_family;
28177 if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
28178
28179 for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
28180 if( !item )
28181 {
28182 NetTransportType type;
28183 uint32_t ifIndex;
28184 const char * const ifName = ifa->ifa_name;
28185
28186 if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
28187 err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
28188 require_noerr( err, exit );
28189
28190 if( ifIndex == 0 ) continue;
28191 if( type == kNetTransportType_AWDL )
28192 {
28193 if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
28194 }
28195 else
28196 {
28197 if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
28198 }
28199 item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
28200 require_action( item, exit, err = kNoMemoryErr );
28201
28202 *ptr = item;
28203 ptr = &item->next;
28204
28205 item->ifName = strdup( ifName );
28206 require_action( item->ifName, exit, err = kNoMemoryErr );
28207
28208 item->ifIndex = ifIndex;
28209 if( type == kNetTransportType_AWDL ) item->isAWDL = true;
28210 else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
28211 }
28212 if( family == AF_INET ) item->hasIPv4 = true;
28213 else item->hasIPv6 = true;
28214 }
28215 require_action_quiet( interfaceList, exit, err = kNotFoundErr );
28216
28217 if( outList )
28218 {
28219 *outList = interfaceList;
28220 interfaceList = NULL;
28221 }
28222
28223 exit:
28224 if( ifaList ) freeifaddrs( ifaList );
28225 _MDNSInterfaceListFree( interfaceList );
28226 ForgetSocket( &infoSock );
28227 return( err );
28228 }
28229
28230 static Boolean _MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
28231 {
28232 OSStatus err;
28233 int i;
28234 static const char * const kMDNSInterfacePrefixBlacklist[] = { "llw", "nan" };
28235 struct ifreq ifr;
28236
28237 // Check if the interface name's prefix matches the prefix blacklist.
28238
28239 for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
28240 {
28241 const char * const prefix = kMDNSInterfacePrefixBlacklist[ i ];
28242
28243 if( strcmp_prefix( inIfName, prefix ) == 0 )
28244 {
28245 const char * ptr = &inIfName[ strlen( prefix ) ];
28246
28247 while( isdigit_safe( *ptr ) ) ++ptr;
28248 if( *ptr == '\0' ) return( true );
28249 }
28250 }
28251
28252 // Check if the interface is used for inter-(co)processor networking.
28253
28254 memset( &ifr, 0, sizeof( ifr ) );
28255 strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
28256 err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
28257 err = map_global_value_errno( err != -1, err );
28258 if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
28259
28260 return( false );
28261 }
28262
28263 //===========================================================================================================================
28264 // _MDNSInterfaceListFree
28265 //===========================================================================================================================
28266
28267 static void _MDNSInterfaceListFree( MDNSInterfaceItem *inList )
28268 {
28269 MDNSInterfaceItem * item;
28270
28271 while( ( item = inList ) != NULL )
28272 {
28273 inList = item->next;
28274 FreeNullSafe( item->ifName );
28275 free( item );
28276 }
28277 }
28278
28279 //===========================================================================================================================
28280 // _MDNSInterfaceGetAny
28281 //===========================================================================================================================
28282
28283 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
28284 {
28285 OSStatus err;
28286 MDNSInterfaceItem * list;
28287 const MDNSInterfaceItem * item;
28288
28289 list = NULL;
28290 err = _MDNSInterfaceListCreate( inSubset, 0, &list );
28291 require_noerr_quiet( err, exit );
28292 require_action_quiet( list, exit, err = kNotFoundErr );
28293
28294 for( item = list; item; item = item->next )
28295 {
28296 if( item->hasIPv4 && item->hasIPv6 ) break;
28297 }
28298 if( !item ) item = list;
28299 if( inNameBuf ) strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
28300 if( outIndex ) *outIndex = item->ifIndex;
28301
28302 exit:
28303 _MDNSInterfaceListFree( list );
28304 return( err );
28305 }
28306
28307 //===========================================================================================================================
28308 // _SetComputerName
28309 //===========================================================================================================================
28310
28311 static OSStatus _SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
28312 {
28313 OSStatus err;
28314 SCPreferencesRef prefs;
28315 Boolean ok;
28316
28317 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
28318 err = map_scerror( prefs );
28319 require_noerr_quiet( err, exit );
28320
28321 ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
28322 err = map_scerror( ok );
28323 require_noerr_quiet( err, exit );
28324
28325 ok = SCPreferencesCommitChanges( prefs );
28326 err = map_scerror( ok );
28327 require_noerr_quiet( err, exit );
28328
28329 ok = SCPreferencesApplyChanges( prefs );
28330 err = map_scerror( ok );
28331 require_noerr_quiet( err, exit );
28332
28333 exit:
28334 CFReleaseNullSafe( prefs );
28335 return( err );
28336 }
28337
28338 //===========================================================================================================================
28339 // _SetComputerNameWithUTF8CString
28340 //===========================================================================================================================
28341
28342 static OSStatus _SetComputerNameWithUTF8CString( const char *inComputerName )
28343 {
28344 OSStatus err;
28345 CFStringRef computerName;
28346
28347 computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
28348 require_action( computerName, exit, err = kNoMemoryErr );
28349
28350 err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
28351 require_noerr_quiet( err, exit );
28352
28353 exit:
28354 CFReleaseNullSafe( computerName );
28355 return( err );
28356 }
28357
28358 //===========================================================================================================================
28359 // _SetLocalHostName
28360 //===========================================================================================================================
28361
28362 static OSStatus _SetLocalHostName( CFStringRef inLocalHostName )
28363 {
28364 OSStatus err;
28365 SCPreferencesRef prefs;
28366 Boolean ok;
28367
28368 prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
28369 err = map_scerror( prefs );
28370 require_noerr_quiet( err, exit );
28371
28372 ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
28373 err = map_scerror( ok );
28374 require_noerr_quiet( err, exit );
28375
28376 ok = SCPreferencesCommitChanges( prefs );
28377 err = map_scerror( ok );
28378 require_noerr_quiet( err, exit );
28379
28380 ok = SCPreferencesApplyChanges( prefs );
28381 err = map_scerror( ok );
28382 require_noerr_quiet( err, exit );
28383
28384 exit:
28385 CFReleaseNullSafe( prefs );
28386 return( err );
28387 }
28388
28389 //===========================================================================================================================
28390 // _SetLocalHostNameWithUTF8CString
28391 //===========================================================================================================================
28392
28393 static OSStatus _SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
28394 {
28395 OSStatus err;
28396 CFStringRef localHostName;
28397
28398 localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
28399 require_action( localHostName, exit, err = kNoMemoryErr );
28400
28401 err = _SetLocalHostName( localHostName );
28402 require_noerr_quiet( err, exit );
28403
28404 exit:
28405 CFReleaseNullSafe( localHostName );
28406 return( err );
28407 }
28408
28409 #if( TARGET_OS_DARWIN )
28410 //===========================================================================================================================
28411 // _InterfaceIPv6AddressAdd
28412 //===========================================================================================================================
28413
28414 static OSStatus _InterfaceIPv6AddressAdd( const char *inIfName, uint8_t inAddr[ STATIC_PARAM 16 ], int inMaskBitLen )
28415 {
28416 OSStatus err;
28417 SocketRef infoSock = kInvalidSocketRef;
28418 struct in6_aliasreq ifra;
28419 size_t len;
28420 struct sockaddr_in6 * sin6;
28421 int wholeBytes, remainingBits;
28422
28423 require_action_quiet( ( inMaskBitLen >= 0 ) && ( inMaskBitLen <= 128 ), exit, err = kSizeErr );
28424
28425 infoSock = socket( AF_INET6, SOCK_DGRAM, 0 );
28426 err = map_socket_creation_errno( infoSock );
28427 require_noerr( err, exit );
28428
28429 // Set interface name.
28430
28431 memset( &ifra, 0, sizeof( ifra ) );
28432 len = strlcpy( ifra.ifra_name, inIfName, sizeof( ifra.ifra_name ) );
28433 require_action_quiet( len < sizeof( ifra.ifra_name ), exit, err = kSizeErr );
28434
28435 // Set IPv6 address.
28436
28437 sin6 = &ifra.ifra_addr;
28438 SIN6_LEN_SET( sin6 );
28439 sin6->sin6_family = AF_INET6;
28440 memcpy( sin6->sin6_addr.s6_addr, inAddr, 16 );
28441
28442 // Set prefix mask.
28443
28444 sin6 = &ifra.ifra_prefixmask;
28445 SIN6_LEN_SET( sin6 );
28446 sin6->sin6_family = AF_INET6;
28447 wholeBytes = inMaskBitLen / 8;
28448 if( wholeBytes > 0 ) memset( sin6->sin6_addr.s6_addr, 0xFF, (size_t) wholeBytes );
28449 remainingBits = inMaskBitLen % 8;
28450 if( remainingBits > 0 ) sin6->sin6_addr.s6_addr[ wholeBytes ] = ( 0xFFU << ( 8 - remainingBits ) ) & 0xFFU;
28451
28452 ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
28453 ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
28454
28455 err = ioctl( infoSock, SIOCAIFADDR_IN6, &ifra );
28456 err = map_global_value_errno( err != -1, err );
28457 require_noerr_quiet( err, exit );
28458
28459 exit:
28460 ForgetSocket( &infoSock );
28461 return( err );
28462 }
28463
28464 //===========================================================================================================================
28465 // _InterfaceIPv6AddressRemove
28466 //===========================================================================================================================
28467
28468 static OSStatus _InterfaceIPv6AddressRemove( const char *inIfName, const uint8_t inAddr[ STATIC_PARAM 16 ] )
28469 {
28470 OSStatus err;
28471 SocketRef infoSock = kInvalidSocketRef;
28472 struct in6_ifreq ifr;
28473 size_t len;
28474 struct sockaddr_in6 * sin6;
28475
28476 infoSock = socket( AF_INET6, SOCK_DGRAM, 0 );
28477 err = map_socket_creation_errno( infoSock );
28478 require_noerr( err, exit );
28479
28480 memset( &ifr, 0, sizeof( ifr ) );
28481 len = strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
28482 require_action_quiet( len < sizeof( ifr.ifr_name ), exit, err = kSizeErr );
28483
28484 sin6 = &ifr.ifr_ifru.ifru_addr;
28485 SIN6_LEN_SET( sin6 );
28486 sin6->sin6_family = AF_INET6;
28487 memcpy( sin6->sin6_addr.s6_addr, inAddr, 16 );
28488
28489 err = ioctl( infoSock, SIOCDIFADDR_IN6, &ifr );
28490 err = map_global_value_errno( err != -1, err );
28491 require_noerr_quiet( err, exit );
28492
28493 exit:
28494 ForgetSocket( &infoSock );
28495 return( err );
28496 }
28497 #endif // TARGET_OS_DARWIN
28498
28499 //===========================================================================================================================
28500 // _TicksDiff
28501 //===========================================================================================================================
28502
28503 static int64_t _TicksDiff( uint64_t inT1, uint64_t inT2 )
28504 {
28505 return( (int64_t)( inT1 - inT2 ) );
28506 }
28507
28508 //===========================================================================================================================
28509 // _SockAddrInitIPv4
28510 //===========================================================================================================================
28511
28512 static void _SockAddrInitIPv4( struct sockaddr_in *inSA, uint32_t inIPv4, uint16_t inPort )
28513 {
28514 memset( inSA, 0, sizeof( *inSA ) );
28515 SIN_LEN_SET( inSA );
28516 inSA->sin_family = AF_INET;
28517 inSA->sin_port = htons( inPort );
28518 inSA->sin_addr.s_addr = htonl( inIPv4 );
28519 }
28520
28521 //===========================================================================================================================
28522 // _SockAddrInitIPv6
28523 //===========================================================================================================================
28524
28525 static void
28526 _SockAddrInitIPv6(
28527 struct sockaddr_in6 * inSA,
28528 const uint8_t inIPv6[ STATIC_PARAM 16 ],
28529 uint32_t inScope,
28530 uint16_t inPort )
28531 {
28532 check_compile_time_code( sizeof( inSA->sin6_addr.s6_addr ) == 16 );
28533
28534 memset( inSA, 0, sizeof( *inSA ) );
28535 SIN6_LEN_SET( inSA );
28536 inSA->sin6_family = AF_INET6;
28537 inSA->sin6_port = htons( inPort );
28538 memcpy( inSA->sin6_addr.s6_addr, inIPv6, 16 );
28539 inSA->sin6_scope_id = inScope;
28540 }
28541
28542 //===========================================================================================================================
28543 // _WriteReverseIPv6DomainNameString
28544 //===========================================================================================================================
28545
28546 static void
28547 _WriteReverseIPv6DomainNameString(
28548 const uint8_t inIPv6Addr[ STATIC_PARAM 16 ],
28549 char outBuffer[ STATIC_PARAM kReverseIPv6DomainNameBufLen ] )
28550 {
28551 char * dst;
28552 int i;
28553
28554 dst = outBuffer;
28555 for( i = 0; i < 16; ++i )
28556 {
28557 const unsigned int octet = inIPv6Addr[ 15 - i ];
28558
28559 *dst++ = kHexDigitsLowercase[ octet & 0x0F ];
28560 *dst++ = '.';
28561 *dst++ = kHexDigitsLowercase[ octet >> 4 ];
28562 *dst++ = '.';
28563 }
28564 memcpy( dst, kIP6ArpaDomainStr, sizeof( kIP6ArpaDomainStr ) );
28565 dst += sizeof( kIP6ArpaDomainStr );
28566 check( ( dst - outBuffer ) == kReverseIPv6DomainNameBufLen );
28567 }
28568
28569 //===========================================================================================================================
28570 // _WriteReverseIPv4DomainNameString
28571 //===========================================================================================================================
28572
28573 static void
28574 _WriteReverseIPv4DomainNameString(
28575 uint32_t inIPv4Addr,
28576 char outBuffer[ STATIC_PARAM kReverseIPv4DomainNameBufLen ] )
28577 {
28578 SNPrintF( outBuffer, kReverseIPv4DomainNameBufLen, "%u.%u.%u.%u.%s",
28579 inIPv4Addr & 0xFF,
28580 ( inIPv4Addr >> 8 ) & 0xFF,
28581 ( inIPv4Addr >> 16 ) & 0xFF,
28582 ( inIPv4Addr >> 24 ) & 0xFF, kInAddrArpaDomainStr );
28583 }
28584
28585 #if( MDNSRESPONDER_PROJECT )
28586 //===========================================================================================================================
28587 // _SetDefaultFallbackDNSService
28588 //===========================================================================================================================
28589
28590 static OSStatus _SetDefaultFallbackDNSService( const char *inFallbackDNSServiceStr )
28591 {
28592 OSStatus err;
28593
28594 if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
28595 {
28596 nw_resolver_config_t resolverConfig;
28597 CFDataRef plistData;
28598
28599 if( stricmp_prefix( inFallbackDNSServiceStr, kFallbackDNSServiceArgPrefix_DoH ) == 0 )
28600 {
28601 nw_endpoint_t endpoint;
28602 const char * const url = inFallbackDNSServiceStr + sizeof_string( kFallbackDNSServiceArgPrefix_DoH );
28603
28604 endpoint = nw_endpoint_create_url( url );
28605 require_action( endpoint, exit, err = kUnknownErr );
28606
28607 resolverConfig = nw_resolver_config_create_https( endpoint );
28608 nw_forget( &endpoint );
28609 require_action( resolverConfig, exit, err = kUnknownErr );
28610 }
28611 else if( stricmp_prefix( inFallbackDNSServiceStr, kFallbackDNSServiceArgPrefix_DoT ) == 0 )
28612 {
28613 nw_endpoint_t endpoint;
28614 const char * const hostname = inFallbackDNSServiceStr + sizeof_string( kFallbackDNSServiceArgPrefix_DoT );
28615
28616 endpoint = nw_endpoint_create_host( hostname, "0" );
28617 require_action( endpoint, exit, err = kUnknownErr );
28618
28619 resolverConfig = nw_resolver_config_create_tls( endpoint );
28620 nw_forget( &endpoint );
28621 require_action( resolverConfig, exit, err = kUnknownErr );
28622 }
28623 else
28624 {
28625 FPrintF( stderr, "error: Unrecognized fallback DNS service string: \"%s\"\n", inFallbackDNSServiceStr );
28626 err = kParamErr;
28627 goto exit;
28628 }
28629 plistData = nw_resolver_config_copy_plist_data_ref( resolverConfig );
28630 require_action( plistData, exit, err = kUnknownErr );
28631
28632 err = DNSServiceSetResolverDefaults( CFDataGetBytePtr( plistData ), (size_t) CFDataGetLength( plistData ), true );
28633 ForgetCF( &plistData );
28634 require_noerr( err, exit );
28635 }
28636 else
28637 {
28638 FPrintF( stderr, "error: Setting a default fallback DNS service is unsupported on this OS.\n" );
28639 err = kUnsupportedErr;
28640 }
28641
28642 exit:
28643 return( err );
28644 }
28645 #endif
28646
28647 //===========================================================================================================================
28648 // MDNSColliderCreate
28649 //===========================================================================================================================
28650
28651 typedef enum
28652 {
28653 kMDNSColliderOpCode_Invalid = 0,
28654 kMDNSColliderOpCode_Send = 1,
28655 kMDNSColliderOpCode_Wait = 2,
28656 kMDNSColliderOpCode_SetProbeActions = 3,
28657 kMDNSColliderOpCode_LoopPush = 4,
28658 kMDNSColliderOpCode_LoopPop = 5,
28659 kMDNSColliderOpCode_Exit = 6
28660
28661 } MDNSColliderOpCode;
28662
28663 typedef struct
28664 {
28665 MDNSColliderOpCode opcode;
28666 uint32_t operand;
28667
28668 } MDNSCInstruction;
28669
28670 #define kMaxLoopDepth 16
28671
28672 struct MDNSColliderPrivate
28673 {
28674 CFRuntimeBase base; // CF object base.
28675 dispatch_queue_t queue; // Queue for collider's events.
28676 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
28677 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
28678 SocketRef sockV4; // IPv4 UDP socket for mDNS.
28679 SocketRef sockV6; // IPv6 UDP socket for mDNS.
28680 uint8_t * target; // Record name being targeted. (malloced)
28681 uint8_t * responsePtr; // Response message pointer. (malloced)
28682 size_t responseLen; // Response message length.
28683 uint8_t * probePtr; // Probe query message pointer. (malloced)
28684 size_t probeLen; // Probe query message length.
28685 unsigned int probeCount; // Count of probe queries received for collider's record.
28686 uint32_t probeActionMap; // Bitmap of actions to take for
28687 MDNSCInstruction * program; // Program to execute.
28688 uint32_t pc; // Program's program counter.
28689 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
28690 uint32_t loopDepth; // Current loop depth.
28691 dispatch_source_t waitTimer; // Timer for program's wait commands.
28692 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
28693 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
28694 void * stopContext; // User's stop handler context.
28695 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
28696 Boolean stopped; // True if the collider has been stopped.
28697 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
28698 };
28699
28700 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
28701 static void _MDNSColliderReadHandler( void *inContext );
28702 static void _MDNSColliderExecuteProgram( void *inContext );
28703 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
28704 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
28705
28706 CF_CLASS_DEFINE( MDNSCollider );
28707
28708 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
28709 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
28710
28711 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
28712 {
28713 OSStatus err;
28714 MDNSColliderRef obj = NULL;
28715
28716 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
28717
28718 ReplaceDispatchQueue( &obj->queue, inQueue );
28719 obj->sockV4 = kInvalidSocketRef;
28720 obj->sockV6 = kInvalidSocketRef;
28721
28722 *outCollider = obj;
28723 err = kNoErr;
28724
28725 exit:
28726 return( err );
28727 }
28728
28729 //===========================================================================================================================
28730 // _MDNSColliderFinalize
28731 //===========================================================================================================================
28732
28733 static void _MDNSColliderFinalize( CFTypeRef inObj )
28734 {
28735 MDNSColliderRef const me = (MDNSColliderRef) inObj;
28736
28737 check( !me->waitTimer );
28738 check( !me->readSourceV4 );
28739 check( !me->readSourceV6 );
28740 check( !IsValidSocket( me->sockV4 ) );
28741 check( !IsValidSocket( me->sockV6 ) );
28742 ForgetMem( &me->target );
28743 ForgetMem( &me->responsePtr );
28744 ForgetMem( &me->probePtr );
28745 ForgetMem( &me->program );
28746 dispatch_forget( &me->queue );
28747 }
28748
28749 //===========================================================================================================================
28750 // MDNSColliderStart
28751 //===========================================================================================================================
28752
28753 static void _MDNSColliderStart( void *inContext );
28754
28755 static OSStatus MDNSColliderStart( MDNSColliderRef me )
28756 {
28757 OSStatus err;
28758
28759 require_action_quiet( me->target, exit, err = kNotPreparedErr );
28760 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
28761 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
28762 require_action_quiet( me->program, exit, err = kNotPreparedErr );
28763 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
28764 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
28765
28766 CFRetain( me );
28767 dispatch_async_f( me->queue, me, _MDNSColliderStart );
28768 err = kNoErr;
28769
28770 exit:
28771 return( err );
28772 }
28773
28774 static void _MDNSColliderStart( void *inContext )
28775 {
28776 OSStatus err;
28777 MDNSColliderRef const me = (MDNSColliderRef) inContext;
28778 SocketRef sock = kInvalidSocketRef;
28779 SocketContext * sockCtx = NULL;
28780
28781 if( me->protocols & kMDNSColliderProtocol_IPv4 )
28782 {
28783 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
28784 require_noerr( err, exit );
28785
28786 sockCtx = SocketContextCreate( sock, me, &err );
28787 require_noerr( err, exit );
28788 sock = kInvalidSocketRef;
28789
28790 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
28791 sockCtx, &me->readSourceV4 );
28792 require_noerr( err, exit );
28793 me->sockV4 = sockCtx->sock;
28794 sockCtx = NULL;
28795
28796 dispatch_resume( me->readSourceV4 );
28797 }
28798
28799 if( me->protocols & kMDNSColliderProtocol_IPv6 )
28800 {
28801 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
28802 require_noerr( err, exit );
28803
28804 sockCtx = SocketContextCreate( sock, me, &err );
28805 require_noerr( err, exit );
28806 sock = kInvalidSocketRef;
28807
28808 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
28809 sockCtx, &me->readSourceV6 );
28810 require_noerr( err, exit );
28811 me->sockV6 = sockCtx->sock;
28812 sockCtx = NULL;
28813
28814 dispatch_resume( me->readSourceV6 );
28815 }
28816
28817 _MDNSColliderExecuteProgram( me );
28818 err = kNoErr;
28819
28820 exit:
28821 ForgetSocket( &sock );
28822 ForgetSocketContext( &sockCtx );
28823 if( err ) _MDNSColliderStop( me, err );
28824 }
28825
28826 //===========================================================================================================================
28827 // MDNSColliderStop
28828 //===========================================================================================================================
28829
28830 static void _MDNSColliderUserStop( void *inContext );
28831
28832 static void MDNSColliderStop( MDNSColliderRef me )
28833 {
28834 CFRetain( me );
28835 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
28836 }
28837
28838 static void _MDNSColliderUserStop( void *inContext )
28839 {
28840 MDNSColliderRef const me = (MDNSColliderRef) inContext;
28841
28842 _MDNSColliderStop( me, kCanceledErr );
28843 CFRelease( me );
28844 }
28845
28846 //===========================================================================================================================
28847 // MDNSColliderSetProtocols
28848 //===========================================================================================================================
28849
28850 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
28851 {
28852 me->protocols = inProtocols;
28853 }
28854
28855 //===========================================================================================================================
28856 // MDNSColliderSetInterfaceIndex
28857 //===========================================================================================================================
28858
28859 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
28860 {
28861 me->interfaceIndex = inInterfaceIndex;
28862 }
28863
28864 //===========================================================================================================================
28865 // MDNSColliderSetProgram
28866 //===========================================================================================================================
28867
28868 #define kMDNSColliderProgCmd_Done "done"
28869 #define kMDNSColliderProgCmd_Loop "loop"
28870 #define kMDNSColliderProgCmd_Send "send"
28871 #define kMDNSColliderProgCmd_Probes "probes"
28872 #define kMDNSColliderProgCmd_Wait "wait"
28873
28874 typedef uint32_t MDNSColliderProbeAction;
28875
28876 #define kMDNSColliderProbeAction_None 0
28877 #define kMDNSColliderProbeAction_Respond 1
28878 #define kMDNSColliderProbeAction_RespondUnicast 2
28879 #define kMDNSColliderProbeAction_RespondMulticast 3
28880 #define kMDNSColliderProbeAction_Probe 4
28881 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
28882
28883 #define kMDNSColliderProbeActionBits_Count 3
28884 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
28885 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
28886
28887 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
28888
28889 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
28890
28891 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
28892 {
28893 OSStatus err;
28894 uint32_t insCount;
28895 unsigned int loopDepth;
28896 const char * cmd;
28897 const char * end;
28898 const char * next;
28899 MDNSCInstruction * program = NULL;
28900 uint32_t loopStart[ kMaxLoopDepth ];
28901
28902 insCount = 0;
28903 for( cmd = inProgramStr; *cmd; cmd = next )
28904 {
28905 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
28906 require_action_quiet( end != cmd, exit, err = kMalformedErr );
28907 next = ( *end == ';' ) ? ( end + 1 ) : end;
28908 ++insCount;
28909 }
28910
28911 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
28912 require_action( program, exit, err = kNoMemoryErr );
28913
28914 insCount = 0;
28915 loopDepth = 0;
28916 for( cmd = inProgramStr; *cmd; cmd = next )
28917 {
28918 size_t cmdLen;
28919 const char * ptr;
28920 const char * arg;
28921 size_t argLen;
28922 uint32_t value;
28923 MDNSCInstruction * const ins = &program[ insCount ];
28924
28925 while( isspace_safe( *cmd ) ) ++cmd;
28926 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
28927 next = ( *end == ';' ) ? ( end + 1 ) : end;
28928
28929 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
28930 cmdLen = (size_t)( ptr - cmd );
28931
28932 // Done statement
28933
28934 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
28935 {
28936 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28937 require_action_quiet( ptr == end, exit, err = kMalformedErr );
28938
28939 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
28940
28941 ins->opcode = kMDNSColliderOpCode_LoopPop;
28942 ins->operand = loopStart[ --loopDepth ];
28943 }
28944
28945 // Loop command
28946
28947 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
28948 {
28949 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
28950 err = DecimalTextToUInt32( arg, end, &value, &ptr );
28951 require_noerr_quiet( err, exit );
28952 require_action_quiet( value > 0, exit, err = kValueErr );
28953
28954 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28955 require_action_quiet( ptr == end, exit, err = kMalformedErr );
28956
28957 ins->opcode = kMDNSColliderOpCode_LoopPush;
28958 ins->operand = value;
28959
28960 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
28961 loopStart[ loopDepth++ ] = insCount + 1;
28962 }
28963
28964 // Probes command
28965
28966 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
28967 {
28968 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
28969 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
28970 argLen = (size_t)( ptr - arg );
28971 if( argLen > 0 )
28972 {
28973 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
28974 require_noerr_quiet( err, exit );
28975 }
28976 else
28977 {
28978 value = 0;
28979 }
28980
28981 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28982 require_action_quiet( ptr == end, exit, err = kMalformedErr );
28983
28984 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
28985 ins->operand = value;
28986 }
28987
28988 // Send command
28989
28990 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
28991 {
28992 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28993 require_action_quiet( ptr == end, exit, err = kMalformedErr );
28994
28995 ins->opcode = kMDNSColliderOpCode_Send;
28996 }
28997
28998 // Wait command
28999
29000 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
29001 {
29002 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
29003 err = DecimalTextToUInt32( arg, end, &value, &ptr );
29004 require_noerr_quiet( err, exit );
29005
29006 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
29007 require_action_quiet( ptr == end, exit, err = kMalformedErr );
29008
29009 ins->opcode = kMDNSColliderOpCode_Wait;
29010 ins->operand = value;
29011 }
29012
29013 // Unrecognized command
29014
29015 else
29016 {
29017 err = kCommandErr;
29018 goto exit;
29019 }
29020 ++insCount;
29021 }
29022 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
29023
29024 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
29025
29026 FreeNullSafe( me->program );
29027 me->program = program;
29028 program = NULL;
29029 err = kNoErr;
29030
29031 exit:
29032 FreeNullSafe( program );
29033 return( err );
29034 }
29035
29036 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
29037 {
29038 OSStatus err;
29039 const char * ptr;
29040 const char * const end = &inString[ inLen ];
29041 uint32_t bitmap;
29042 int index;
29043
29044 bitmap = 0;
29045 index = 0;
29046 ptr = inString;
29047 while( ptr < end )
29048 {
29049 int c, count;
29050 MDNSColliderProbeAction action;
29051
29052 c = *ptr++;
29053 if( isdigit_safe( c ) )
29054 {
29055 count = 0;
29056 do
29057 {
29058 count = ( count * 10 ) + ( c - '0' );
29059 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
29060 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
29061 c = *ptr++;
29062
29063 } while( isdigit_safe( c ) );
29064 require_action_quiet( count > 0, exit, err = kCountErr );
29065 }
29066 else
29067 {
29068 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
29069 count = 1;
29070 }
29071
29072 switch( c )
29073 {
29074 case 'n': action = kMDNSColliderProbeAction_None; break;
29075 case 'r': action = kMDNSColliderProbeAction_Respond; break;
29076 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
29077 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
29078 case 'p': action = kMDNSColliderProbeAction_Probe; break;
29079 default: err = kMalformedErr; goto exit;
29080 }
29081 if( ptr < end )
29082 {
29083 c = *ptr++;
29084 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
29085 }
29086 while( count-- > 0 )
29087 {
29088 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
29089 ++index;
29090 }
29091 }
29092
29093 *outBitmap = bitmap;
29094 err = kNoErr;
29095
29096 exit:
29097 return( err );
29098 }
29099
29100 //===========================================================================================================================
29101 // MDNSColliderSetStopHandler
29102 //===========================================================================================================================
29103
29104 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
29105 {
29106 me->stopHandler = inStopHandler;
29107 me->stopContext = inStopContext;
29108 }
29109
29110 //===========================================================================================================================
29111 // MDNSColliderSetRecord
29112 //===========================================================================================================================
29113
29114 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
29115 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
29116 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
29117
29118 static OSStatus
29119 MDNSColliderSetRecord(
29120 MDNSColliderRef me,
29121 const uint8_t * inName,
29122 uint16_t inType,
29123 const void * inRDataPtr,
29124 size_t inRDataLen )
29125 {
29126 OSStatus err;
29127 DataBuffer msgDB;
29128 DNSHeader header;
29129 uint8_t * targetPtr = NULL;
29130 size_t targetLen;
29131 uint8_t * responsePtr = NULL;
29132 size_t responseLen;
29133 uint8_t * probePtr = NULL;
29134 size_t probeLen;
29135
29136 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
29137
29138 err = DomainNameDup( inName, &targetPtr, &targetLen );
29139 require_noerr_quiet( err, exit );
29140
29141 // Create response message.
29142
29143 memset( &header, 0, sizeof( header ) );
29144 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
29145 DNSHeaderSetAnswerCount( &header, 1 );
29146
29147 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
29148 require_noerr( err, exit );
29149
29150 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kMDNSClassCacheFlushBit,
29151 1976, inRDataPtr, inRDataLen );
29152 require_noerr( err, exit );
29153
29154 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
29155 require_noerr( err, exit );
29156
29157 // Create probe message.
29158
29159 memset( &header, 0, sizeof( header ) );
29160 DNSHeaderSetQuestionCount( &header, 2 );
29161 DNSHeaderSetAuthorityCount( &header, 1 );
29162
29163 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
29164 require_noerr( err, exit );
29165
29166 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
29167 require_noerr( err, exit );
29168
29169 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
29170 kDNSServiceType_NULL, kDNSServiceClass_IN );
29171 require_noerr( err, exit );
29172
29173 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
29174 1976, inRDataPtr, inRDataLen );
29175 require_noerr( err, exit );
29176
29177 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
29178 require_noerr( err, exit );
29179
29180 FreeNullSafe( me->target );
29181 me->target = targetPtr;
29182 targetPtr = NULL;
29183
29184 FreeNullSafe( me->responsePtr );
29185 me->responsePtr = responsePtr;
29186 me->responseLen = responseLen;
29187 responsePtr = NULL;
29188
29189 FreeNullSafe( me->probePtr );
29190 me->probePtr = probePtr;
29191 me->probeLen = probeLen;
29192 probePtr = NULL;
29193
29194 exit:
29195 DataBuffer_Free( &msgDB );
29196 FreeNullSafe( targetPtr );
29197 FreeNullSafe( responsePtr );
29198 FreeNullSafe( probePtr );
29199 return( err );
29200 }
29201
29202 //===========================================================================================================================
29203 // _MDNSColliderStop
29204 //===========================================================================================================================
29205
29206 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
29207 {
29208 dispatch_source_forget( &me->waitTimer );
29209 dispatch_source_forget( &me->readSourceV4 );
29210 dispatch_source_forget( &me->readSourceV6 );
29211 me->sockV4 = kInvalidSocketRef;
29212 me->sockV6 = kInvalidSocketRef;
29213
29214 if( !me->stopped )
29215 {
29216 me->stopped = true;
29217 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
29218 CFRelease( me );
29219 }
29220 }
29221
29222 //===========================================================================================================================
29223 // _MDNSColliderReadHandler
29224 //===========================================================================================================================
29225
29226 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
29227 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
29228
29229 static void _MDNSColliderReadHandler( void *inContext )
29230 {
29231 OSStatus err;
29232 struct timeval now;
29233 SocketContext * const sockCtx = (SocketContext *) inContext;
29234 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
29235 size_t msgLen;
29236 sockaddr_ip sender;
29237 const DNSHeader * hdr;
29238 const uint8_t * ptr;
29239 const struct sockaddr * dest;
29240 int probeFound, probeIsQU;
29241 unsigned int qCount, i;
29242 MDNSColliderProbeAction action;
29243
29244 gettimeofday( &now, NULL );
29245
29246 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
29247 NULL, NULL, NULL, NULL );
29248 require_noerr( err, exit );
29249
29250 require_quiet( msgLen >= kDNSHeaderLength, exit );
29251 hdr = (const DNSHeader *) me->msgBuf;
29252
29253 probeFound = false;
29254 probeIsQU = false;
29255 qCount = DNSHeaderGetQuestionCount( hdr );
29256 ptr = (const uint8_t *) &hdr[ 1 ];
29257 for( i = 0; i < qCount; ++i )
29258 {
29259 uint16_t qtype, qclass;
29260 uint8_t qname[ kDomainNameLengthMax ];
29261
29262 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
29263 require_noerr_quiet( err, exit );
29264
29265 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
29266 DomainNameEqual( qname, kMDNSColliderDummyName ) )
29267 {
29268 probeFound = false;
29269 break;
29270 }
29271
29272 if( qtype != kDNSServiceType_ANY ) continue;
29273 if( ( qclass & ~kMDNSClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
29274 if( !DomainNameEqual( qname, me->target ) ) continue;
29275
29276 if( !probeFound )
29277 {
29278 probeFound = true;
29279 probeIsQU = ( qclass & kMDNSClassUnicastResponseBit ) ? true : false;
29280 }
29281 }
29282 require_quiet( probeFound, exit );
29283
29284 ++me->probeCount;
29285 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
29286
29287 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s) -- %#.1{du:dnsmsg}\n",
29288 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
29289
29290 if( ( action == kMDNSColliderProbeAction_Respond ) ||
29291 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
29292 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
29293 {
29294 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
29295 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
29296 {
29297 dest = &sender.sa;
29298 }
29299 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
29300 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
29301 {
29302 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
29303 }
29304
29305 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
29306 require_noerr( err, exit );
29307 }
29308 else if( action == kMDNSColliderProbeAction_Probe )
29309 {
29310 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
29311
29312 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
29313 require_noerr( err, exit );
29314 }
29315
29316 exit:
29317 return;
29318 }
29319
29320 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
29321 {
29322 MDNSColliderProbeAction action;
29323
29324 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
29325 {
29326 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
29327 kMDNSColliderProbeActionBits_Mask;
29328 }
29329 else
29330 {
29331 action = kMDNSColliderProbeAction_None;
29332 }
29333 return( action );
29334 }
29335
29336 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
29337 {
29338 switch( inAction )
29339 {
29340 case kMDNSColliderProbeAction_None: return( "None" );
29341 case kMDNSColliderProbeAction_Respond: return( "Respond" );
29342 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
29343 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
29344 case kMDNSColliderProbeAction_Probe: return( "Probe" );
29345 default: return( "???" );
29346 }
29347 }
29348
29349 //===========================================================================================================================
29350 // _MDNSColliderExecuteProgram
29351 //===========================================================================================================================
29352
29353 static void _MDNSColliderExecuteProgram( void *inContext )
29354 {
29355 OSStatus err;
29356 MDNSColliderRef const me = (MDNSColliderRef) inContext;
29357 int stop;
29358
29359 dispatch_forget( &me->waitTimer );
29360
29361 stop = false;
29362 for( ;; )
29363 {
29364 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
29365 uint32_t waitMs;
29366
29367 switch( ins->opcode )
29368 {
29369 case kMDNSColliderOpCode_Send:
29370 if( IsValidSocket( me->sockV4 ) )
29371 {
29372 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
29373 require_noerr( err, exit );
29374 }
29375 if( IsValidSocket( me->sockV6 ) )
29376 {
29377 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
29378 require_noerr( err, exit );
29379 }
29380 break;
29381
29382 case kMDNSColliderOpCode_Wait:
29383 waitMs = ins->operand;
29384 if( waitMs > 0 )
29385 {
29386 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
29387 _MDNSColliderExecuteProgram, me, &me->waitTimer );
29388 require_noerr( err, exit );
29389 dispatch_resume( me->waitTimer );
29390 goto exit;
29391 }
29392 break;
29393
29394 case kMDNSColliderOpCode_SetProbeActions:
29395 me->probeCount = 0;
29396 me->probeActionMap = ins->operand;
29397 break;
29398
29399 case kMDNSColliderOpCode_LoopPush:
29400 check( me->loopDepth < kMaxLoopDepth );
29401 me->loopCounts[ me->loopDepth++ ] = ins->operand;
29402 break;
29403
29404 case kMDNSColliderOpCode_LoopPop:
29405 check( me->loopDepth > 0 );
29406 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
29407 {
29408 me->pc = ins->operand;
29409 }
29410 else
29411 {
29412 --me->loopDepth;
29413 }
29414 break;
29415
29416 case kMDNSColliderOpCode_Exit:
29417 stop = true;
29418 err = kNoErr;
29419 goto exit;
29420
29421 default:
29422 dlogassert( "Unhandled opcode %u\n", ins->opcode );
29423 err = kCommandErr;
29424 goto exit;
29425 }
29426 }
29427
29428 exit:
29429 if( err || stop ) _MDNSColliderStop( me, err );
29430 }
29431
29432 //===========================================================================================================================
29433 // _MDNSColliderSendResponse
29434 //===========================================================================================================================
29435
29436 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
29437 {
29438 OSStatus err;
29439 ssize_t n;
29440
29441 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
29442 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
29443 return( err );
29444 }
29445
29446 //===========================================================================================================================
29447 // _MDNSColliderSendProbe
29448 //===========================================================================================================================
29449
29450 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
29451 {
29452 OSStatus err;
29453 ssize_t n;
29454
29455 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
29456 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
29457 return( err );
29458 }
29459
29460 //===========================================================================================================================
29461 // ServiceBrowserCreate
29462 //===========================================================================================================================
29463
29464 typedef struct SBDomain SBDomain;
29465 typedef struct SBServiceType SBServiceType;
29466 typedef struct SBServiceBrowse SBServiceBrowse;
29467 typedef struct SBServiceInstance SBServiceInstance;
29468 typedef struct SBIPAddress SBIPAddress;
29469
29470 struct ServiceBrowserPrivate
29471 {
29472 CFRuntimeBase base; // CF object base.
29473 dispatch_queue_t queue; // Queue for service browser's events.
29474 DNSServiceRef connection; // Shared connection for DNS-SD ops.
29475 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
29476 char * domain; // If non-null, then browsing is limited to this domain.
29477 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
29478 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
29479 void * userContext; // User's callback context.
29480 SBDomain * domainList; // List of domains and their browse results.
29481 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
29482 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
29483 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
29484 #if( MDNSRESPONDER_PROJECT )
29485 Boolean useNewGAI; // Use dnssd_getaddrinfo_* instead of DNSServiceGetAddrInfo().
29486 #endif
29487 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
29488 // use the "any" interface.
29489 };
29490
29491 struct SBDomain
29492 {
29493 SBDomain * next; // Next domain object in list.
29494 ServiceBrowserRef browser; // Pointer to parent service browser.
29495 char * name; // Name of the domain.
29496 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
29497 SBServiceType * typeList; // List of service types to browse for in this domain.
29498 };
29499
29500 struct SBServiceType
29501 {
29502 SBServiceType * next; // Next service type object in list.
29503 char * name; // Name of the service type.
29504 SBServiceBrowse * browseList; // List of browses for this service type.
29505 };
29506
29507 struct SBServiceBrowse
29508 {
29509 SBServiceBrowse * next; // Next browse object in list.
29510 ServiceBrowserRef browser; // Pointer to parent service browser.
29511 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
29512 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
29513 uint64_t startTicks; // Value of UpTicks() when the browse op began.
29514 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
29515 };
29516
29517 struct SBServiceInstance
29518 {
29519 SBServiceInstance * next; // Next service instance object in list.
29520 ServiceBrowserRef browser; // Pointer to parent service browser.
29521 char * name; // Name of the service instance.
29522 char * fqdn; // Fully qualified domain name of service instance (for logging/debugging).
29523 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
29524 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
29525 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
29526 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
29527 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
29528 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
29529 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
29530 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
29531 size_t txtLen; // Length of service instance's TXT record data.
29532 DNSServiceRef gai; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
29533 #if( MDNSRESPONDER_PROJECT )
29534 dnssd_getaddrinfo_t newGAI; // Reference to dnssd_getaddrinfo object for service instance's hostname.
29535 #endif
29536 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
29537 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
29538 int32_t refCount; // This object's reference count.
29539 };
29540
29541 struct SBIPAddress
29542 {
29543 SBIPAddress * next; // Next IP address object in list.
29544 sockaddr_ip sip; // IPv4 or IPv6 address.
29545 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
29546 };
29547
29548 typedef struct
29549 {
29550 SBRDomain * domainList; // List of domains in which services were found.
29551 int32_t refCount; // This object's reference count.
29552
29553 } ServiceBrowserResultsPrivate;
29554
29555 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
29556 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
29557 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
29558 static void _ServiceBrowserTimerHandler( void *inContext );
29559 static void DNSSD_API
29560 _ServiceBrowserDomainsQueryCallback(
29561 DNSServiceRef inSDRef,
29562 DNSServiceFlags inFlags,
29563 uint32_t inInterfaceIndex,
29564 DNSServiceErrorType inError,
29565 const char * inFullName,
29566 uint16_t inType,
29567 uint16_t inClass,
29568 uint16_t inRDataLen,
29569 const void * inRDataPtr,
29570 uint32_t inTTL,
29571 void * inContext );
29572 static void DNSSD_API
29573 _ServiceBrowserServicesQueryCallback(
29574 DNSServiceRef inSDRef,
29575 DNSServiceFlags inFlags,
29576 uint32_t inInterfaceIndex,
29577 DNSServiceErrorType inError,
29578 const char * inFullName,
29579 uint16_t inType,
29580 uint16_t inClass,
29581 uint16_t inRDataLen,
29582 const void * inRDataPtr,
29583 uint32_t inTTL,
29584 void * inContext );
29585 static void DNSSD_API
29586 _ServiceBrowserBrowseCallback(
29587 DNSServiceRef inSDRef,
29588 DNSServiceFlags inFlags,
29589 uint32_t inInterfaceIndex,
29590 DNSServiceErrorType inError,
29591 const char * inName,
29592 const char * inRegType,
29593 const char * inDomain,
29594 void * inContext );
29595 static void DNSSD_API
29596 _ServiceBrowserResolveCallback(
29597 DNSServiceRef inSDRef,
29598 DNSServiceFlags inFlags,
29599 uint32_t inInterfaceIndex,
29600 DNSServiceErrorType inError,
29601 const char * inFullName,
29602 const char * inHostname,
29603 uint16_t inPort,
29604 uint16_t inTXTLen,
29605 const unsigned char * inTXTPtr,
29606 void * inContext );
29607 #if( MDNSRESPONDER_PROJECT )
29608 static void
29609 _ServiceBrowserGAIResultHandler(
29610 ServiceBrowserRef inBrowser,
29611 SBServiceInstance * inInstance,
29612 dnssd_getaddrinfo_result_t * inResultArray,
29613 size_t inResultCount );
29614 #endif
29615 static void DNSSD_API
29616 _ServiceBrowserGAICallback(
29617 DNSServiceRef inSDRef,
29618 DNSServiceFlags inFlags,
29619 uint32_t inInterfaceIndex,
29620 DNSServiceErrorType inError,
29621 const char * inHostname,
29622 const struct sockaddr * inSockAddr,
29623 uint32_t inTTL,
29624 void * inContext );
29625 static OSStatus
29626 _ServiceBrowserAddServiceType(
29627 ServiceBrowserRef inBrowser,
29628 SBDomain * inDomain,
29629 const char * inName,
29630 uint32_t inIfIndex );
29631 static OSStatus
29632 _ServiceBrowserRemoveServiceType(
29633 ServiceBrowserRef inBrowser,
29634 SBDomain * inDomain,
29635 const char * inName,
29636 uint32_t inIfIndex );
29637 static OSStatus
29638 _ServiceBrowserAddServiceInstance(
29639 ServiceBrowserRef inBrowser,
29640 SBServiceBrowse * inBrowse,
29641 uint32_t inIfIndex,
29642 const char * inName,
29643 const char * inRegType,
29644 const char * inDomain,
29645 uint64_t inDiscoverTimeUs );
29646 static OSStatus
29647 _ServiceBrowserRemoveServiceInstance(
29648 ServiceBrowserRef inBrowser,
29649 SBServiceBrowse * inBrowse,
29650 const char * inName,
29651 uint32_t inIfIndex );
29652 static OSStatus
29653 _ServiceBrowserAddIPAddress(
29654 ServiceBrowserRef inBrowser,
29655 SBServiceInstance * inInstance,
29656 const struct sockaddr * inSockAddr,
29657 uint64_t inResolveTimeUs );
29658 static OSStatus
29659 _ServiceBrowserRemoveIPAddress(
29660 ServiceBrowserRef inBrowser,
29661 SBServiceInstance * inInstance,
29662 const struct sockaddr * inSockAddr );
29663 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
29664 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
29665 static void _SBDomainFree( SBDomain *inDomain );
29666 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
29667 static void _SBServiceTypeFree( SBServiceType *inType );
29668 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
29669 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
29670 static OSStatus
29671 _SBServiceInstanceCreate(
29672 const char * inName,
29673 const char * inType,
29674 const char * inDomain,
29675 uint32_t inIfIndex,
29676 uint64_t inDiscoverTimeUs,
29677 ServiceBrowserRef inBrowser,
29678 SBServiceInstance ** outInstance );
29679 #if( MDNSRESPONDER_PROJECT )
29680 static void _SBServiceInstanceRetain( SBServiceInstance *inInstance );
29681 #endif
29682 static void _SBServiceInstanceStop( SBServiceInstance *inInstance );
29683 static void _SBServiceInstanceRelease( SBServiceInstance *inInstance );
29684 #define _SBServiceInstanceForget( X ) ForgetCustomEx( X, _SBServiceInstanceStop, _SBServiceInstanceRelease )
29685 static OSStatus
29686 _SBIPAddressCreate(
29687 const struct sockaddr * inSockAddr,
29688 uint64_t inResolveTimeUs,
29689 SBIPAddress ** outIPAddress );
29690 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
29691 static void _SBIPAddressFreeList( SBIPAddress *inList );
29692 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
29693 static void _SBRDomainFree( SBRDomain *inDomain );
29694 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
29695 static void _SBRServiceTypeFree( SBRServiceType *inType );
29696 static OSStatus
29697 _SBRServiceInstanceCreate(
29698 const char * inName,
29699 uint32_t inInterfaceIndex,
29700 const char * inHostname,
29701 uint16_t inPort,
29702 const uint8_t * inTXTPtr,
29703 size_t inTXTLen,
29704 uint64_t inDiscoverTimeUs,
29705 uint64_t inResolveTimeUs,
29706 SBRServiceInstance ** outInstance );
29707 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
29708 static OSStatus
29709 _SBRIPAddressCreate(
29710 const struct sockaddr * inSockAddr,
29711 uint64_t inResolveTimeUs,
29712 SBRIPAddress ** outIPAddress );
29713 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
29714
29715 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
29716
29717 CF_CLASS_DEFINE( ServiceBrowser );
29718
29719 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
29720 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
29721
29722 static OSStatus
29723 ServiceBrowserCreate(
29724 dispatch_queue_t inQueue,
29725 uint32_t inInterfaceIndex,
29726 const char * inDomain,
29727 unsigned int inBrowseTimeSecs,
29728 Boolean inIncludeAWDL,
29729 ServiceBrowserRef * outBrowser )
29730 {
29731 OSStatus err;
29732 ServiceBrowserRef obj;
29733
29734 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
29735
29736 ReplaceDispatchQueue( &obj->queue, inQueue );
29737 obj->ifIndex = inInterfaceIndex;
29738 if( inDomain )
29739 {
29740 obj->domain = strdup( inDomain );
29741 require_action( obj->domain, exit, err = kNoMemoryErr );
29742 }
29743 obj->browseTimeSecs = inBrowseTimeSecs;
29744 obj->includeAWDL = inIncludeAWDL;
29745
29746 *outBrowser = obj;
29747 obj = NULL;
29748 err = kNoErr;
29749
29750 exit:
29751 CFReleaseNullSafe( obj );
29752 return( err );
29753 }
29754
29755 //===========================================================================================================================
29756 // _ServiceBrowserFinalize
29757 //===========================================================================================================================
29758
29759 static void _ServiceBrowserFinalize( CFTypeRef inObj )
29760 {
29761 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
29762 StringListItem * serviceType;
29763
29764 dispatch_forget( &me->queue );
29765 check( !me->connection );
29766 check( !me->domainsQuery );
29767 ForgetMem( &me->domain );
29768 while( ( serviceType = me->serviceTypeList ) != NULL )
29769 {
29770 me->serviceTypeList = serviceType->next;
29771 ForgetMem( &serviceType->str );
29772 free( serviceType );
29773 }
29774 check( !me->domainList );
29775 check( !me->stopTimer );
29776 }
29777
29778 #if( MDNSRESPONDER_PROJECT )
29779 //===========================================================================================================================
29780 // ServiceBrowserSetUseNewGAI
29781 //===========================================================================================================================
29782
29783 static void ServiceBrowserSetUseNewGAI( ServiceBrowserRef me, Boolean inUseNewGAI )
29784 {
29785 me->useNewGAI = inUseNewGAI;
29786 }
29787 #endif
29788
29789 //===========================================================================================================================
29790 // ServiceBrowserStart
29791 //===========================================================================================================================
29792
29793 static void _ServiceBrowserStart( void *inContext );
29794
29795 static void ServiceBrowserStart( ServiceBrowserRef me )
29796 {
29797 CFRetain( me );
29798 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
29799 }
29800
29801 static void _ServiceBrowserStart( void *inContext )
29802 {
29803 OSStatus err;
29804 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
29805
29806 err = DNSServiceCreateConnection( &me->connection );
29807 require_noerr( err, exit );
29808
29809 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
29810 require_noerr( err, exit );
29811
29812 if( me->domain )
29813 {
29814 err = _ServiceBrowserAddDomain( me, me->domain );
29815 require_noerr( err, exit );
29816 }
29817 else
29818 {
29819 DNSServiceRef sdRef;
29820 const char * const recordName = "b._dns-sd._udp.local.";
29821 const uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
29822
29823 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
29824 // See <https://tools.ietf.org/html/rfc6763#section-11>.
29825
29826 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
29827
29828 sdRef = me->connection;
29829 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
29830 kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
29831 require_noerr( err, exit );
29832
29833 me->domainsQuery = sdRef;
29834 }
29835
29836 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
29837 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
29838 require_noerr( err, exit );
29839 dispatch_resume( me->stopTimer );
29840
29841 exit:
29842 if( err ) _ServiceBrowserStop( me, err );
29843 }
29844
29845 //===========================================================================================================================
29846 // ServiceBrowserAddServiceType
29847 //===========================================================================================================================
29848
29849 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
29850 {
29851 OSStatus err;
29852 StringListItem * item;
29853 StringListItem ** itemPtr;
29854 StringListItem * newItem = NULL;
29855
29856 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
29857 {
29858 if( strcmp( item->str, inServiceType ) == 0 ) break;
29859 }
29860 if( !item )
29861 {
29862 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
29863 require_action( newItem, exit, err = kNoMemoryErr );
29864
29865 newItem->str = strdup( inServiceType );
29866 require_action( newItem->str, exit, err = kNoMemoryErr );
29867
29868 *itemPtr = newItem;
29869 newItem = NULL;
29870 }
29871 err = kNoErr;
29872
29873 exit:
29874 FreeNullSafe( newItem );
29875 return( err );
29876 }
29877
29878 //===========================================================================================================================
29879 // ServiceBrowserSetCallback
29880 //===========================================================================================================================
29881
29882 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
29883 {
29884 me->userCallback = inCallback;
29885 me->userContext = inContext;
29886 }
29887
29888 //===========================================================================================================================
29889 // ServiceBrowserResultsRetain
29890 //===========================================================================================================================
29891
29892 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
29893 {
29894 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
29895
29896 atomic_add_32( &results->refCount, 1 );
29897 }
29898
29899 //===========================================================================================================================
29900 // ServiceBrowserResultsRelease
29901 //===========================================================================================================================
29902
29903 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
29904 {
29905 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
29906 SBRDomain * domain;
29907
29908 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
29909 {
29910 while( ( domain = inResults->domainList ) != NULL )
29911 {
29912 inResults->domainList = domain->next;
29913 _SBRDomainFree( domain );
29914 }
29915 free( inResults );
29916 }
29917 }
29918
29919 //===========================================================================================================================
29920 // _ServiceBrowserStop
29921 //===========================================================================================================================
29922
29923 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
29924 {
29925 OSStatus err;
29926 SBDomain * d;
29927
29928 dispatch_source_forget( &me->stopTimer );
29929 DNSServiceForget( &me->domainsQuery );
29930 for( d = me->domainList; d; d = d->next )
29931 {
29932 SBServiceType * t;
29933
29934 DNSServiceForget( &d->servicesQuery );
29935 for( t = d->typeList; t; t = t->next )
29936 {
29937 SBServiceBrowse * b;
29938
29939 for( b = t->browseList; b; b = b->next )
29940 {
29941 SBServiceInstance * i;
29942
29943 DNSServiceForget( &b->browse );
29944 for( i = b->instanceList; i; i = i->next )
29945 {
29946 _SBServiceInstanceStop( i );
29947 }
29948 }
29949 }
29950 }
29951 DNSServiceForget( &me->connection );
29952
29953 if( me->userCallback )
29954 {
29955 ServiceBrowserResults * results = NULL;
29956
29957 err = _ServiceBrowserCreateResults( me, &results );
29958 if( !err ) err = inError;
29959
29960 me->userCallback( results, err, me->userContext );
29961 me->userCallback = NULL;
29962 me->userContext = NULL;
29963 if( results ) ServiceBrowserResultsRelease( results );
29964 }
29965
29966 while( ( d = me->domainList ) != NULL )
29967 {
29968 me->domainList = d->next;
29969 _SBDomainFree( d );
29970 }
29971 CFRelease( me );
29972 }
29973
29974 //===========================================================================================================================
29975 // _ServiceBrowserAddDomain
29976 //===========================================================================================================================
29977
29978 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
29979 {
29980 OSStatus err;
29981 SBDomain * domain;
29982 SBDomain ** domainPtr;
29983 SBDomain * newDomain = NULL;
29984
29985 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
29986 {
29987 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
29988 }
29989 require_action_quiet( !domain, exit, err = kDuplicateErr );
29990
29991 err = _SBDomainCreate( inDomain, me, &newDomain );
29992 require_noerr_quiet( err, exit );
29993
29994 if( me->serviceTypeList )
29995 {
29996 const StringListItem * item;
29997
29998 for( item = me->serviceTypeList; item; item = item->next )
29999 {
30000 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
30001 if( err == kDuplicateErr ) err = kNoErr;
30002 require_noerr( err, exit );
30003 }
30004 }
30005 else
30006 {
30007 char * recordName;
30008 DNSServiceRef sdRef;
30009 DNSServiceFlags flags;
30010
30011 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
30012 // See <https://tools.ietf.org/html/rfc6763#section-9>.
30013
30014 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
30015 require_action( recordName, exit, err = kNoMemoryErr );
30016
30017 flags = kDNSServiceFlagsShareConnection;
30018 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
30019
30020 sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
30021
30022 sdRef = newDomain->browser->connection;
30023 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
30024 _ServiceBrowserServicesQueryCallback, newDomain );
30025 free( recordName );
30026 require_noerr( err, exit );
30027
30028 newDomain->servicesQuery = sdRef;
30029 }
30030
30031 *domainPtr = newDomain;
30032 newDomain = NULL;
30033 err = kNoErr;
30034
30035 exit:
30036 if( newDomain ) _SBDomainFree( newDomain );
30037 return( err );
30038 }
30039
30040 //===========================================================================================================================
30041 // _ServiceBrowserRemoveDomain
30042 //===========================================================================================================================
30043
30044 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
30045 {
30046 OSStatus err;
30047 SBDomain * domain;
30048 SBDomain ** domainPtr;
30049
30050 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
30051 {
30052 if( strcasecmp( domain->name, inName ) == 0 ) break;
30053 }
30054
30055 if( domain )
30056 {
30057 *domainPtr = domain->next;
30058 _SBDomainFree( domain );
30059 err = kNoErr;
30060 }
30061 else
30062 {
30063 err = kNotFoundErr;
30064 }
30065
30066 return( err );
30067 }
30068
30069 //===========================================================================================================================
30070 // _ServiceBrowserTimerHandler
30071 //===========================================================================================================================
30072
30073 static void _ServiceBrowserTimerHandler( void *inContext )
30074 {
30075 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
30076
30077 _ServiceBrowserStop( me, kNoErr );
30078 }
30079
30080 //===========================================================================================================================
30081 // _ServiceBrowserDomainsQueryCallback
30082 //===========================================================================================================================
30083
30084 static void DNSSD_API
30085 _ServiceBrowserDomainsQueryCallback(
30086 DNSServiceRef inSDRef,
30087 DNSServiceFlags inFlags,
30088 uint32_t inInterfaceIndex,
30089 DNSServiceErrorType inError,
30090 const char * inFullName,
30091 uint16_t inType,
30092 uint16_t inClass,
30093 uint16_t inRDataLen,
30094 const void * inRDataPtr,
30095 uint32_t inTTL,
30096 void * inContext )
30097 {
30098 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
30099 OSStatus err;
30100 char domainStr[ kDNSServiceMaxDomainName ];
30101
30102 Unused( inSDRef );
30103 Unused( inClass );
30104 Unused( inTTL );
30105
30106 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
30107 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
30108 !inError, inError );
30109
30110 require_noerr( inError, exit );
30111
30112 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
30113 require_noerr( err, exit );
30114
30115 if( inFlags & kDNSServiceFlagsAdd )
30116 {
30117 err = _ServiceBrowserAddDomain( me, domainStr );
30118 if( err == kDuplicateErr ) err = kNoErr;
30119 require_noerr( err, exit );
30120 }
30121 else
30122 {
30123 err = _ServiceBrowserRemoveDomain( me, domainStr );
30124 if( err == kNotFoundErr ) err = kNoErr;
30125 require_noerr( err, exit );
30126 }
30127
30128 exit:
30129 return;
30130 }
30131
30132 //===========================================================================================================================
30133 // _ServiceBrowserServicesQueryCallback
30134 //===========================================================================================================================
30135
30136 static void DNSSD_API
30137 _ServiceBrowserServicesQueryCallback(
30138 DNSServiceRef inSDRef,
30139 DNSServiceFlags inFlags,
30140 uint32_t inInterfaceIndex,
30141 DNSServiceErrorType inError,
30142 const char * inFullName,
30143 uint16_t inType,
30144 uint16_t inClass,
30145 uint16_t inRDataLen,
30146 const void * inRDataPtr,
30147 uint32_t inTTL,
30148 void * inContext )
30149 {
30150 OSStatus err;
30151 SBDomain * const domain = (SBDomain *) inContext;
30152 ServiceBrowserRef const me = domain->browser;
30153 const uint8_t * src;
30154 const uint8_t * end;
30155 uint8_t * dst;
30156 int i;
30157 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
30158 char serviceTypeStr[ kDNSServiceMaxDomainName ];
30159
30160 Unused( inSDRef );
30161 Unused( inTTL );
30162 Unused( inClass );
30163
30164 sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
30165 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
30166 !inError, inError );
30167
30168 require_noerr( inError, exit );
30169
30170 check( inType == kDNSServiceType_PTR );
30171 check( inClass == kDNSServiceClass_IN );
30172
30173 // The first two labels of the domain name in the RDATA describe a service type.
30174 // See <https://tools.ietf.org/html/rfc6763#section-9>.
30175
30176 src = (const uint8_t *) inRDataPtr;
30177 end = src + inRDataLen;
30178 dst = serviceType;
30179 for( i = 0; i < 2; ++i )
30180 {
30181 size_t labelLen;
30182
30183 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
30184
30185 labelLen = *src;
30186 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
30187 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
30188
30189 memcpy( dst, src, 1 + labelLen );
30190 src += 1 + labelLen;
30191 dst += 1 + labelLen;
30192 }
30193 *dst = 0;
30194
30195 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
30196 require_noerr( err, exit );
30197
30198 if( inFlags & kDNSServiceFlagsAdd )
30199 {
30200 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
30201 if( err == kDuplicateErr ) err = kNoErr;
30202 require_noerr( err, exit );
30203 }
30204 else
30205 {
30206 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
30207 if( err == kNotFoundErr ) err = kNoErr;
30208 require_noerr( err, exit );
30209 }
30210
30211 exit:
30212 return;
30213 }
30214
30215 //===========================================================================================================================
30216 // _ServiceBrowserBrowseCallback
30217 //===========================================================================================================================
30218
30219 static void DNSSD_API
30220 _ServiceBrowserBrowseCallback(
30221 DNSServiceRef inSDRef,
30222 DNSServiceFlags inFlags,
30223 uint32_t inInterfaceIndex,
30224 DNSServiceErrorType inError,
30225 const char * inName,
30226 const char * inRegType,
30227 const char * inDomain,
30228 void * inContext )
30229 {
30230 OSStatus err;
30231 const uint64_t nowTicks = UpTicks();
30232 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
30233 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
30234
30235 Unused( inSDRef );
30236
30237 sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
30238 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
30239
30240 require_noerr( inError, exit );
30241
30242 if( inFlags & kDNSServiceFlagsAdd )
30243 {
30244 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
30245 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
30246 if( err == kDuplicateErr ) err = kNoErr;
30247 require_noerr( err, exit );
30248 }
30249 else
30250 {
30251 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
30252 if( err == kNotFoundErr ) err = kNoErr;
30253 require_noerr( err, exit );
30254 }
30255
30256 exit:
30257 return;
30258 }
30259
30260 //===========================================================================================================================
30261 // _ServiceBrowserResolveCallback
30262 //===========================================================================================================================
30263
30264 static void DNSSD_API
30265 _ServiceBrowserResolveCallback(
30266 DNSServiceRef inSDRef,
30267 DNSServiceFlags inFlags,
30268 uint32_t inInterfaceIndex,
30269 DNSServiceErrorType inError,
30270 const char * inFullName,
30271 const char * inHostname,
30272 uint16_t inPort,
30273 uint16_t inTXTLen,
30274 const unsigned char * inTXTPtr,
30275 void * inContext )
30276 {
30277 OSStatus err;
30278 const uint64_t nowTicks = UpTicks();
30279 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
30280 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
30281
30282 Unused( inSDRef );
30283 Unused( inFlags );
30284
30285 sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
30286 (int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
30287
30288 require_noerr( inError, exit );
30289
30290 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
30291 {
30292 FreeNullSafe( instance->txtPtr );
30293 instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
30294 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
30295
30296 instance->txtLen = inTXTLen;
30297 }
30298
30299 instance->port = ntohs( inPort );
30300
30301 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
30302 {
30303 DNSServiceRef sdRef;
30304
30305 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
30306
30307 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
30308 require_noerr( err, exit );
30309
30310 sb_ulog( kLogLevelTrace,
30311 "Starting GetAddrInfo on interface %d for %s", (int32_t) instance->ifIndex, instance->hostname );
30312
30313 ForgetSBIPAddressList( &instance->ipaddrList );
30314
30315 #if( MDNSRESPONDER_PROJECT )
30316 if( me->useNewGAI )
30317 {
30318 dnssd_getaddrinfo_t gai;
30319
30320 dnssd_getaddrinfo_forget( &instance->newGAI );
30321
30322 gai = dnssd_getaddrinfo_create();
30323 require_action( gai, exit, err = kNoResourcesErr );
30324
30325 dnssd_getaddrinfo_set_hostname( gai, instance->hostname );
30326 dnssd_getaddrinfo_set_flags( gai, 0 );
30327 dnssd_getaddrinfo_set_interface_index( gai, instance->ifIndex );
30328 dnssd_getaddrinfo_set_protocols( gai, kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6 );
30329 dnssd_getaddrinfo_set_queue( gai, me->queue );
30330 _SBServiceInstanceRetain( instance );
30331 CFRetain( me );
30332 dnssd_getaddrinfo_set_result_handler( gai,
30333 ^( dnssd_getaddrinfo_result_t * const inResultArray, const size_t inResultCount )
30334 {
30335 if( instance->newGAI == gai )
30336 {
30337 _ServiceBrowserGAIResultHandler( me, instance, inResultArray, inResultCount );
30338 }
30339 } );
30340 dnssd_getaddrinfo_set_event_handler( gai,
30341 ^( dnssd_event_t inEvent, DNSServiceErrorType inGAIError )
30342 {
30343 switch( inEvent )
30344 {
30345 case dnssd_event_invalidated:
30346 dnssd_release( gai );
30347 _SBServiceInstanceRelease( instance );
30348 CFRelease( me );
30349 break;
30350
30351 case dnssd_event_error:
30352 if( instance->newGAI == gai )
30353 {
30354 sb_ulog( kLogLevelError, "dnssd_getaddrinfo error %#m\n", inGAIError );
30355 dnssd_getaddrinfo_forget( &instance->newGAI );
30356 }
30357 break;
30358
30359 default:
30360 break;
30361 }
30362 } );
30363 instance->newGAI = gai;
30364 dnssd_retain( instance->newGAI );
30365 dnssd_getaddrinfo_activate( instance->newGAI );
30366 }
30367 else
30368 #endif
30369 {
30370 DNSServiceForget( &instance->gai );
30371
30372 sdRef = me->connection;
30373 instance->gaiStartTicks = UpTicks();
30374 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
30375 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback,
30376 instance );
30377 require_noerr( err, exit );
30378
30379 instance->gai = sdRef;
30380 }
30381 }
30382
30383 exit:
30384 return;
30385 }
30386
30387 #if( MDNSRESPONDER_PROJECT )
30388 //===========================================================================================================================
30389 // _ServiceBrowserGAIResultHandler
30390 //===========================================================================================================================
30391
30392 static void
30393 _ServiceBrowserGAIResultHandler(
30394 ServiceBrowserRef me,
30395 SBServiceInstance * inInstance,
30396 dnssd_getaddrinfo_result_t * inResultArray,
30397 size_t inResultCount )
30398 {
30399 OSStatus err;
30400 size_t i;
30401 const uint64_t nowTicks = UpTicks();
30402
30403 for( i = 0; i < inResultCount; ++i )
30404 {
30405 const dnssd_getaddrinfo_result_t result = inResultArray[ i ];
30406 const dnssd_getaddrinfo_result_type_t type = dnssd_getaddrinfo_result_get_type( result );
30407
30408 sb_ulog( kLogLevelTrace, "dnssd_getaddrinfo result: %@\n", result );
30409
30410 if( type == dnssd_getaddrinfo_result_type_add )
30411 {
30412 err = _ServiceBrowserAddIPAddress( me, inInstance, dnssd_getaddrinfo_result_get_address( result ),
30413 UpTicksToMicroseconds( nowTicks - inInstance->gaiStartTicks ) );
30414 if( err == kDuplicateErr ) err = kNoErr;
30415 require_noerr( err, exit );
30416 }
30417 else if( type == dnssd_getaddrinfo_result_type_remove )
30418 {
30419 err = _ServiceBrowserRemoveIPAddress( me, inInstance, dnssd_getaddrinfo_result_get_address( result ) );
30420 if( err == kNotFoundErr ) err = kNoErr;
30421 require_noerr( err, exit );
30422 }
30423 }
30424
30425 exit:
30426 return;
30427 }
30428 #endif
30429
30430 //===========================================================================================================================
30431 // _ServiceBrowserGAICallback
30432 //===========================================================================================================================
30433
30434 static void DNSSD_API
30435 _ServiceBrowserGAICallback(
30436 DNSServiceRef inSDRef,
30437 DNSServiceFlags inFlags,
30438 uint32_t inInterfaceIndex,
30439 DNSServiceErrorType inError,
30440 const char * inHostname,
30441 const struct sockaddr * inSockAddr,
30442 uint32_t inTTL,
30443 void * inContext )
30444 {
30445 OSStatus err;
30446 const uint64_t nowTicks = UpTicks();
30447 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
30448 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
30449
30450 Unused( inSDRef );
30451 Unused( inTTL );
30452
30453 sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
30454 DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
30455 !inError, inError );
30456
30457 require_noerr( inError, exit );
30458
30459 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
30460 {
30461 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
30462 goto exit;
30463 }
30464
30465 if( inFlags & kDNSServiceFlagsAdd )
30466 {
30467 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
30468 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
30469 if( err == kDuplicateErr ) err = kNoErr;
30470 require_noerr( err, exit );
30471 }
30472 else
30473 {
30474 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
30475 if( err == kNotFoundErr ) err = kNoErr;
30476 require_noerr( err, exit );
30477 }
30478
30479 exit:
30480 return;
30481 }
30482
30483 //===========================================================================================================================
30484 // _ServiceBrowserAddServiceType
30485 //===========================================================================================================================
30486
30487 static OSStatus
30488 _ServiceBrowserAddServiceType(
30489 ServiceBrowserRef me,
30490 SBDomain * inDomain,
30491 const char * inName,
30492 uint32_t inIfIndex )
30493 {
30494 OSStatus err;
30495 SBServiceType * type;
30496 SBServiceType ** typePtr;
30497 SBServiceType * newType = NULL;
30498 SBServiceBrowse * browse;
30499 SBServiceBrowse ** browsePtr;
30500 SBServiceBrowse * newBrowse = NULL;
30501 DNSServiceRef sdRef;
30502 DNSServiceFlags flags;
30503
30504 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
30505 {
30506 if( strcasecmp( type->name, inName ) == 0 ) break;
30507 }
30508 if( !type )
30509 {
30510 err = _SBServiceTypeCreate( inName, &newType );
30511 require_noerr_quiet( err, exit );
30512
30513 type = newType;
30514 }
30515
30516 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
30517 {
30518 if( browse->ifIndex == inIfIndex ) break;
30519 }
30520 require_action_quiet( !browse, exit, err = kDuplicateErr );
30521
30522 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
30523 require_noerr_quiet( err, exit );
30524
30525 flags = kDNSServiceFlagsShareConnection;
30526 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
30527
30528 sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
30529 (int32_t) newBrowse->ifIndex, type->name, inDomain->name );
30530
30531 sdRef = me->connection;
30532 newBrowse->startTicks = UpTicks();
30533 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
30534 newBrowse );
30535 require_noerr( err, exit );
30536
30537 newBrowse->browse = sdRef;
30538 *browsePtr = newBrowse;
30539 newBrowse = NULL;
30540
30541 if( newType )
30542 {
30543 *typePtr = newType;
30544 newType = NULL;
30545 }
30546
30547 exit:
30548 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
30549 if( newType ) _SBServiceTypeFree( newType );
30550 return( err );
30551 }
30552
30553 //===========================================================================================================================
30554 // _ServiceBrowserRemoveServiceType
30555 //===========================================================================================================================
30556
30557 static OSStatus
30558 _ServiceBrowserRemoveServiceType(
30559 ServiceBrowserRef me,
30560 SBDomain * inDomain,
30561 const char * inName,
30562 uint32_t inIfIndex )
30563 {
30564 OSStatus err;
30565 SBServiceType * type;
30566 SBServiceType ** typePtr;
30567 SBServiceBrowse * browse;
30568 SBServiceBrowse ** browsePtr;
30569
30570 Unused( me );
30571
30572 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
30573 {
30574 if( strcasecmp( type->name, inName ) == 0 ) break;
30575 }
30576 require_action_quiet( type, exit, err = kNotFoundErr );
30577
30578 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
30579 {
30580 if( browse->ifIndex == inIfIndex ) break;
30581 }
30582 require_action_quiet( browse, exit, err = kNotFoundErr );
30583
30584 *browsePtr = browse->next;
30585 _SBServiceBrowseFree( browse );
30586 if( !type->browseList )
30587 {
30588 *typePtr = type->next;
30589 _SBServiceTypeFree( type );
30590 }
30591 err = kNoErr;
30592
30593 exit:
30594 return( err );
30595 }
30596
30597 //===========================================================================================================================
30598 // _ServiceBrowserAddServiceInstance
30599 //===========================================================================================================================
30600
30601 static OSStatus
30602 _ServiceBrowserAddServiceInstance(
30603 ServiceBrowserRef me,
30604 SBServiceBrowse * inBrowse,
30605 uint32_t inIfIndex,
30606 const char * inName,
30607 const char * inRegType,
30608 const char * inDomain,
30609 uint64_t inDiscoverTimeUs )
30610 {
30611 OSStatus err;
30612 DNSServiceRef sdRef;
30613 SBServiceInstance * instance;
30614 SBServiceInstance ** instancePtr;
30615 SBServiceInstance * newInstance = NULL;
30616
30617 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
30618 {
30619 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
30620 }
30621 require_action_quiet( !instance, exit, err = kDuplicateErr );
30622
30623 err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
30624 require_noerr_quiet( err, exit );
30625
30626 sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
30627 (int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
30628
30629 sdRef = me->connection;
30630 newInstance->resolveStartTicks = UpTicks();
30631 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
30632 _ServiceBrowserResolveCallback, newInstance );
30633 require_noerr( err, exit );
30634
30635 newInstance->resolve = sdRef;
30636 *instancePtr = newInstance;
30637 newInstance = NULL;
30638
30639 exit:
30640 if( newInstance ) _SBServiceInstanceRelease( newInstance );
30641 return( err );
30642 }
30643
30644 //===========================================================================================================================
30645 // _ServiceBrowserRemoveServiceInstance
30646 //===========================================================================================================================
30647
30648 static OSStatus
30649 _ServiceBrowserRemoveServiceInstance(
30650 ServiceBrowserRef me,
30651 SBServiceBrowse * inBrowse,
30652 const char * inName,
30653 uint32_t inIfIndex )
30654 {
30655 OSStatus err;
30656 SBServiceInstance * instance;
30657 SBServiceInstance ** ptr;
30658
30659 Unused( me );
30660
30661 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
30662 {
30663 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
30664 }
30665 require_action_quiet( instance, exit, err = kNotFoundErr );
30666
30667 *ptr = instance->next;
30668 _SBServiceInstanceForget( &instance );
30669 err = kNoErr;
30670
30671 exit:
30672 return( err );
30673 }
30674
30675 //===========================================================================================================================
30676 // _ServiceBrowserAddIPAddress
30677 //===========================================================================================================================
30678
30679 static OSStatus
30680 _ServiceBrowserAddIPAddress(
30681 ServiceBrowserRef me,
30682 SBServiceInstance * inInstance,
30683 const struct sockaddr * inSockAddr,
30684 uint64_t inResolveTimeUs )
30685 {
30686 OSStatus err;
30687 SBIPAddress * ipaddr;
30688 SBIPAddress ** ipaddrPtr;
30689 SBIPAddress * newIPAddr = NULL;
30690
30691 Unused( me );
30692
30693 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
30694 {
30695 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
30696 err = kTypeErr;
30697 goto exit;
30698 }
30699
30700 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
30701 {
30702 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
30703 }
30704 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
30705
30706 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
30707 require_noerr_quiet( err, exit );
30708
30709 *ipaddrPtr = newIPAddr;
30710 newIPAddr = NULL;
30711 err = kNoErr;
30712
30713 exit:
30714 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
30715 return( err );
30716 }
30717
30718 //===========================================================================================================================
30719 // _ServiceBrowserRemoveIPAddress
30720 //===========================================================================================================================
30721
30722 static OSStatus
30723 _ServiceBrowserRemoveIPAddress(
30724 ServiceBrowserRef me,
30725 SBServiceInstance * inInstance,
30726 const struct sockaddr * inSockAddr )
30727 {
30728 OSStatus err;
30729 SBIPAddress * ipaddr;
30730 SBIPAddress ** ipaddrPtr;
30731
30732 Unused( me );
30733
30734 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
30735 {
30736 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
30737 }
30738 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
30739
30740 *ipaddrPtr = ipaddr->next;
30741 _SBIPAddressFree( ipaddr );
30742 err = kNoErr;
30743
30744 exit:
30745 return( err );
30746 }
30747
30748 //===========================================================================================================================
30749 // _ServiceBrowserCreateResults
30750 //===========================================================================================================================
30751
30752 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
30753 {
30754 OSStatus err;
30755 SBDomain * d;
30756 SBServiceType * t;
30757 SBServiceBrowse * b;
30758 SBServiceInstance * i;
30759 SBIPAddress * a;
30760 ServiceBrowserResultsPrivate * results;
30761 SBRDomain ** domainPtr;
30762
30763 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
30764 require_action( results, exit, err = kNoMemoryErr );
30765
30766 results->refCount = 1;
30767
30768 domainPtr = &results->domainList;
30769 for( d = me->domainList; d; d = d->next )
30770 {
30771 SBRDomain * domain;
30772 SBRServiceType ** typePtr;
30773
30774 err = _SBRDomainCreate( d->name, &domain );
30775 require_noerr_quiet( err, exit );
30776 *domainPtr = domain;
30777 domainPtr = &domain->next;
30778
30779 typePtr = &domain->typeList;
30780 for( t = d->typeList; t; t = t->next )
30781 {
30782 SBRServiceType * type;
30783 SBRServiceInstance ** instancePtr;
30784
30785 err = _SBRServiceTypeCreate( t->name, &type );
30786 require_noerr_quiet( err, exit );
30787 *typePtr = type;
30788 typePtr = &type->next;
30789
30790 instancePtr = &type->instanceList;
30791 for( b = t->browseList; b; b = b->next )
30792 {
30793 for( i = b->instanceList; i; i = i->next )
30794 {
30795 SBRServiceInstance * instance;
30796 SBRIPAddress ** ipaddrPtr;
30797
30798 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
30799 i->discoverTimeUs, i->resolveTimeUs, &instance );
30800 require_noerr_quiet( err, exit );
30801 *instancePtr = instance;
30802 instancePtr = &instance->next;
30803
30804 ipaddrPtr = &instance->ipaddrList;
30805 for( a = i->ipaddrList; a; a = a->next )
30806 {
30807 SBRIPAddress * ipaddr;
30808
30809 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
30810 require_noerr_quiet( err, exit );
30811
30812 *ipaddrPtr = ipaddr;
30813 ipaddrPtr = &ipaddr->next;
30814 }
30815 }
30816 }
30817 }
30818 }
30819
30820 *outResults = (ServiceBrowserResults *) results;
30821 results = NULL;
30822 err = kNoErr;
30823
30824 exit:
30825 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
30826 return( err );
30827 }
30828
30829 //===========================================================================================================================
30830 // _SBDomainCreate
30831 //===========================================================================================================================
30832
30833 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
30834 {
30835 OSStatus err;
30836 SBDomain * obj;
30837
30838 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
30839 require_action( obj, exit, err = kNoMemoryErr );
30840
30841 obj->name = strdup( inName );
30842 require_action( obj->name, exit, err = kNoMemoryErr );
30843
30844 obj->browser = inBrowser;
30845
30846 *outDomain = obj;
30847 obj = NULL;
30848 err = kNoErr;
30849
30850 exit:
30851 if( obj ) _SBDomainFree( obj );
30852 return( err );
30853 }
30854
30855 //===========================================================================================================================
30856 // _SBDomainFree
30857 //===========================================================================================================================
30858
30859 static void _SBDomainFree( SBDomain *inDomain )
30860 {
30861 SBServiceType * type;
30862
30863 ForgetMem( &inDomain->name );
30864 DNSServiceForget( &inDomain->servicesQuery );
30865 while( ( type = inDomain->typeList ) != NULL )
30866 {
30867 inDomain->typeList = type->next;
30868 _SBServiceTypeFree( type );
30869 }
30870 free( inDomain );
30871 }
30872
30873 //===========================================================================================================================
30874 // _SBServiceTypeCreate
30875 //===========================================================================================================================
30876
30877 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
30878 {
30879 OSStatus err;
30880 SBServiceType * obj;
30881
30882 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
30883 require_action( obj, exit, err = kNoMemoryErr );
30884
30885 obj->name = strdup( inName );
30886 require_action( obj->name, exit, err = kNoMemoryErr );
30887
30888 *outType = obj;
30889 obj = NULL;
30890 err = kNoErr;
30891
30892 exit:
30893 if( obj ) _SBServiceTypeFree( obj );
30894 return( err );
30895 }
30896
30897 //===========================================================================================================================
30898 // _SBServiceTypeFree
30899 //===========================================================================================================================
30900
30901 static void _SBServiceTypeFree( SBServiceType *inType )
30902 {
30903 SBServiceBrowse * browse;
30904
30905 ForgetMem( &inType->name );
30906 while( ( browse = inType->browseList ) != NULL )
30907 {
30908 inType->browseList = browse->next;
30909 _SBServiceBrowseFree( browse );
30910 }
30911 free( inType );
30912 }
30913
30914 //===========================================================================================================================
30915 // _SBServiceBrowseCreate
30916 //===========================================================================================================================
30917
30918 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
30919 {
30920 OSStatus err;
30921 SBServiceBrowse * obj;
30922
30923 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
30924 require_action( obj, exit, err = kNoMemoryErr );
30925
30926 obj->ifIndex = inIfIndex;
30927 obj->browser = inBrowser;
30928 *outBrowse = obj;
30929 err = kNoErr;
30930
30931 exit:
30932 return( err );
30933 }
30934
30935 //===========================================================================================================================
30936 // _SBServiceBrowseFree
30937 //===========================================================================================================================
30938
30939 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
30940 {
30941 SBServiceInstance * instance;
30942
30943 DNSServiceForget( &inBrowse->browse );
30944 while( ( instance = inBrowse->instanceList ) != NULL )
30945 {
30946 inBrowse->instanceList = instance->next;
30947 _SBServiceInstanceForget( &instance );
30948 }
30949 free( inBrowse );
30950 }
30951
30952 //===========================================================================================================================
30953 // _SBServiceInstanceCreate
30954 //===========================================================================================================================
30955
30956 static OSStatus
30957 _SBServiceInstanceCreate(
30958 const char * inName,
30959 const char * inType,
30960 const char * inDomain,
30961 uint32_t inIfIndex,
30962 uint64_t inDiscoverTimeUs,
30963 ServiceBrowserRef inBrowser,
30964 SBServiceInstance ** outInstance )
30965 {
30966 OSStatus err;
30967 SBServiceInstance * obj;
30968
30969 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
30970 require_action( obj, exit, err = kNoMemoryErr );
30971
30972 obj->refCount = 1;
30973
30974 obj->name = strdup( inName );
30975 require_action( obj->name, exit, err = kNoMemoryErr );
30976
30977 ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
30978 require_action( obj->fqdn, exit, err = kNoMemoryErr );
30979
30980 obj->ifIndex = inIfIndex;
30981 obj->discoverTimeUs = inDiscoverTimeUs;
30982 obj->browser = inBrowser;
30983
30984 *outInstance = obj;
30985 obj = NULL;
30986 err = kNoErr;
30987
30988 exit:
30989 if( obj ) _SBServiceInstanceRelease( obj );
30990 return( err );
30991 }
30992
30993 #if( MDNSRESPONDER_PROJECT )
30994 //===========================================================================================================================
30995 // _SBServiceInstanceRetain
30996 //===========================================================================================================================
30997
30998 static void _SBServiceInstanceRetain( SBServiceInstance *inInstance )
30999 {
31000 atomic_add_32( &inInstance->refCount, 1 );
31001 }
31002 #endif
31003
31004 //===========================================================================================================================
31005 // _SBServiceInstanceStop
31006 //===========================================================================================================================
31007
31008 static void _SBServiceInstanceStop( SBServiceInstance *inInstance )
31009 {
31010 DNSServiceForget( &inInstance->resolve );
31011 DNSServiceForget( &inInstance->gai );
31012 #if( MDNSRESPONDER_PROJECT )
31013 dnssd_getaddrinfo_forget( &inInstance->newGAI );
31014 #endif
31015 }
31016
31017 //===========================================================================================================================
31018 // _SBServiceInstanceRelease
31019 //===========================================================================================================================
31020
31021 static void _SBServiceInstanceRelease( SBServiceInstance *inInstance )
31022 {
31023 if( atomic_add_and_fetch_32( &inInstance->refCount, -1 ) == 0 )
31024 {
31025 check( !inInstance->resolve );
31026 check( !inInstance->gai );
31027 #if( MDNSRESPONDER_PROJECT )
31028 check( !inInstance->newGAI );
31029 #endif
31030 ForgetMem( &inInstance->name );
31031 ForgetMem( &inInstance->fqdn );
31032 ForgetMem( &inInstance->hostname );
31033 ForgetMem( &inInstance->txtPtr );
31034 ForgetSBIPAddressList( &inInstance->ipaddrList );
31035 free( inInstance );
31036 }
31037 }
31038
31039 //===========================================================================================================================
31040 // _SBIPAddressCreate
31041 //===========================================================================================================================
31042
31043 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
31044 {
31045 OSStatus err;
31046 SBIPAddress * obj;
31047
31048 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
31049 require_action( obj, exit, err = kNoMemoryErr );
31050
31051 SockAddrCopy( inSockAddr, &obj->sip );
31052 obj->resolveTimeUs = inResolveTimeUs;
31053
31054 *outIPAddress = obj;
31055 err = kNoErr;
31056
31057 exit:
31058 return( err );
31059 }
31060
31061 //===========================================================================================================================
31062 // _SBIPAddressFree
31063 //===========================================================================================================================
31064
31065 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
31066 {
31067 free( inIPAddress );
31068 }
31069
31070 //===========================================================================================================================
31071 // _SBIPAddressFreeList
31072 //===========================================================================================================================
31073
31074 static void _SBIPAddressFreeList( SBIPAddress *inList )
31075 {
31076 SBIPAddress * ipaddr;
31077
31078 while( ( ipaddr = inList ) != NULL )
31079 {
31080 inList = ipaddr->next;
31081 _SBIPAddressFree( ipaddr );
31082 }
31083 }
31084
31085 //===========================================================================================================================
31086 // _SBRDomainCreate
31087 //===========================================================================================================================
31088
31089 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
31090 {
31091 OSStatus err;
31092 SBRDomain * obj;
31093
31094 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
31095 require_action( obj, exit, err = kNoMemoryErr );
31096
31097 obj->name = strdup( inName );
31098 require_action( obj->name, exit, err = kNoMemoryErr );
31099
31100 *outDomain = obj;
31101 obj = NULL;
31102 err = kNoErr;
31103
31104 exit:
31105 if( obj ) _SBRDomainFree( obj );
31106 return( err );
31107 }
31108
31109 //===========================================================================================================================
31110 // _SBRDomainFree
31111 //===========================================================================================================================
31112
31113 static void _SBRDomainFree( SBRDomain *inDomain )
31114 {
31115 SBRServiceType * type;
31116
31117 ForgetMem( &inDomain->name );
31118 while( ( type = inDomain->typeList ) != NULL )
31119 {
31120 inDomain->typeList = type->next;
31121 _SBRServiceTypeFree( type );
31122 }
31123 free( inDomain );
31124 }
31125
31126 //===========================================================================================================================
31127 // _SBRServiceTypeCreate
31128 //===========================================================================================================================
31129
31130 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
31131 {
31132 OSStatus err;
31133 SBRServiceType * obj;
31134
31135 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
31136 require_action( obj, exit, err = kNoMemoryErr );
31137
31138 obj->name = strdup( inName );
31139 require_action( obj->name, exit, err = kNoMemoryErr );
31140
31141 *outType = obj;
31142 obj = NULL;
31143 err = kNoErr;
31144
31145 exit:
31146 if( obj ) _SBRServiceTypeFree( obj );
31147 return( err );
31148 }
31149
31150 //===========================================================================================================================
31151 // _SBRServiceTypeFree
31152 //===========================================================================================================================
31153
31154 static void _SBRServiceTypeFree( SBRServiceType *inType )
31155 {
31156 SBRServiceInstance * instance;
31157
31158 ForgetMem( &inType->name );
31159 while( ( instance = inType->instanceList ) != NULL )
31160 {
31161 inType->instanceList = instance->next;
31162 _SBRServiceInstanceFree( instance );
31163 }
31164 free( inType );
31165 }
31166
31167 //===========================================================================================================================
31168 // _SBRServiceInstanceCreate
31169 //===========================================================================================================================
31170
31171 static OSStatus
31172 _SBRServiceInstanceCreate(
31173 const char * inName,
31174 uint32_t inInterfaceIndex,
31175 const char * inHostname,
31176 uint16_t inPort,
31177 const uint8_t * inTXTPtr,
31178 size_t inTXTLen,
31179 uint64_t inDiscoverTimeUs,
31180 uint64_t inResolveTimeUs,
31181 SBRServiceInstance ** outInstance )
31182 {
31183 OSStatus err;
31184 SBRServiceInstance * obj;
31185
31186 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
31187 require_action( obj, exit, err = kNoMemoryErr );
31188
31189 obj->name = strdup( inName );
31190 require_action( obj->name, exit, err = kNoMemoryErr );
31191
31192 if( inHostname )
31193 {
31194 obj->hostname = strdup( inHostname );
31195 require_action( obj->hostname, exit, err = kNoMemoryErr );
31196 }
31197 if( inTXTLen > 0 )
31198 {
31199 obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
31200 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
31201 obj->txtLen = inTXTLen;
31202 }
31203 obj->discoverTimeUs = inDiscoverTimeUs;
31204 obj->resolveTimeUs = inResolveTimeUs;
31205 obj->ifIndex = inInterfaceIndex;
31206 obj->port = inPort;
31207
31208 *outInstance = obj;
31209 obj = NULL;
31210 err = kNoErr;
31211
31212 exit:
31213 if( obj ) _SBRServiceInstanceFree( obj );
31214 return( err );
31215 }
31216
31217 //===========================================================================================================================
31218 // _SBRServiceInstanceFree
31219 //===========================================================================================================================
31220
31221 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
31222 {
31223 SBRIPAddress * ipaddr;
31224
31225 ForgetMem( &inInstance->name );
31226 ForgetMem( &inInstance->hostname );
31227 ForgetMem( &inInstance->txtPtr );
31228 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
31229 {
31230 inInstance->ipaddrList = ipaddr->next;
31231 _SBRIPAddressFree( ipaddr );
31232 }
31233 free( inInstance );
31234 }
31235
31236 //===========================================================================================================================
31237 // _SBRIPAddressCreate
31238 //===========================================================================================================================
31239
31240 static OSStatus
31241 _SBRIPAddressCreate(
31242 const struct sockaddr * inSockAddr,
31243 uint64_t inResolveTimeUs,
31244 SBRIPAddress ** outIPAddress )
31245 {
31246 OSStatus err;
31247 SBRIPAddress * obj;
31248
31249 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
31250 require_action( obj, exit, err = kNoMemoryErr );
31251
31252 SockAddrCopy( inSockAddr, &obj->sip );
31253 obj->resolveTimeUs = inResolveTimeUs;
31254
31255 *outIPAddress = obj;
31256 err = kNoErr;
31257
31258 exit:
31259 return( err );
31260 }
31261
31262 //===========================================================================================================================
31263 // _SBRIPAddressFree
31264 //===========================================================================================================================
31265
31266 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
31267 {
31268 free( inIPAddress );
31269 }
31270
31271 //===========================================================================================================================
31272 // _SocketWriteAll
31273 //
31274 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
31275 //===========================================================================================================================
31276
31277 static OSStatus _SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
31278 {
31279 OSStatus err;
31280 const uint8_t * src;
31281 const uint8_t * end;
31282 fd_set writeSet;
31283 struct timeval timeout;
31284 ssize_t n;
31285
31286 FD_ZERO( &writeSet );
31287 src = (const uint8_t *) inData;
31288 end = src + inSize;
31289 while( src < end )
31290 {
31291 FD_SET( inSock, &writeSet );
31292 timeout.tv_sec = inTimeoutSecs;
31293 timeout.tv_usec = 0;
31294 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
31295 if( n == 0 ) { err = kTimeoutErr; goto exit; }
31296 err = map_socket_value_errno( inSock, n > 0, n );
31297 require_noerr( err, exit );
31298
31299 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
31300 err = map_socket_value_errno( inSock, n >= 0, n );
31301 if( err == EINTR ) continue;
31302 require_noerr( err, exit );
31303
31304 src += n;
31305 }
31306 err = kNoErr;
31307
31308 exit:
31309 return( err );
31310 }
31311
31312 //===========================================================================================================================
31313 // _ParseIPv4Address
31314 //
31315 // Warning: "inBuffer" may be modified even in error cases.
31316 //
31317 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
31318 //===========================================================================================================================
31319
31320 static OSStatus _ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
31321 {
31322 OSStatus err;
31323 uint8_t * dst;
31324 int segments;
31325 int sawDigit;
31326 int c;
31327 int v;
31328
31329 check( inBuffer );
31330 check( outStr );
31331
31332 dst = inBuffer;
31333 *dst = 0;
31334 sawDigit = 0;
31335 segments = 0;
31336 for( ; ( c = *inStr ) != '\0'; ++inStr )
31337 {
31338 if( isdigit_safe( c ) )
31339 {
31340 v = ( *dst * 10 ) + ( c - '0' );
31341 require_action_quiet( v <= 255, exit, err = kRangeErr );
31342 *dst = (uint8_t) v;
31343 if( !sawDigit )
31344 {
31345 ++segments;
31346 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
31347 sawDigit = 1;
31348 }
31349 }
31350 else if( ( c == '.' ) && sawDigit )
31351 {
31352 require_action_quiet( segments < 4, exit, err = kMalformedErr );
31353 *++dst = 0;
31354 sawDigit = 0;
31355 }
31356 else
31357 {
31358 break;
31359 }
31360 }
31361 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
31362
31363 *outStr = inStr;
31364 err = kNoErr;
31365
31366 exit:
31367 return( err );
31368 }
31369
31370 //===========================================================================================================================
31371 // _StringToIPv4Address
31372 //
31373 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
31374 //===========================================================================================================================
31375
31376 static OSStatus
31377 _StringToIPv4Address(
31378 const char * inStr,
31379 StringToIPAddressFlags inFlags,
31380 uint32_t * outIP,
31381 int * outPort,
31382 uint32_t * outSubnet,
31383 uint32_t * outRouter,
31384 const char ** outStr )
31385 {
31386 OSStatus err;
31387 uint8_t buf[ 4 ];
31388 int c;
31389 uint32_t ip;
31390 int hasPort;
31391 int port;
31392 int hasPrefix;
31393 int prefix;
31394 uint32_t subnetMask;
31395 uint32_t router;
31396
31397 require_action( inStr, exit, err = kParamErr );
31398
31399 // Parse the address-only part of the address (e.g. "1.2.3.4").
31400
31401 err = _ParseIPv4Address( inStr, buf, &inStr );
31402 require_noerr_quiet( err, exit );
31403 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
31404 c = *inStr;
31405
31406 // Parse the port (if any).
31407
31408 hasPort = 0;
31409 port = 0;
31410 if( c == ':' )
31411 {
31412 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
31413 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
31414 require_action_quiet( port <= 65535, exit, err = kRangeErr );
31415 hasPort = 1;
31416 }
31417
31418 // Parse the prefix length (if any).
31419
31420 hasPrefix = 0;
31421 prefix = 0;
31422 subnetMask = 0;
31423 router = 0;
31424 if( c == '/' )
31425 {
31426 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
31427 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
31428 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
31429 hasPrefix = 1;
31430
31431 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
31432 router = ( ip & subnetMask ) | 1;
31433 }
31434
31435 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
31436
31437 if( outIP ) *outIP = ip;
31438 if( outPort && hasPort ) *outPort = port;
31439 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
31440 if( outRouter && hasPrefix ) *outRouter = router;
31441 if( outStr ) *outStr = inStr;
31442 err = kNoErr;
31443
31444 exit:
31445 return( err );
31446 }
31447
31448 //===========================================================================================================================
31449 // _ParseIPv6Address
31450 //
31451 // Note: Parsed according to the rules specified in RFC 3513.
31452 // Warning: "inBuffer" may be modified even in error cases.
31453 //
31454 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31455 //===========================================================================================================================
31456
31457 static OSStatus _ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
31458 {
31459 // Table to map uppercase hex characters - '0' to their numeric values.
31460 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
31461 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 };
31462 OSStatus err;
31463 const char * ptr;
31464 uint8_t * dst;
31465 uint8_t * lim;
31466 uint8_t * colonPtr;
31467 int c;
31468 int sawDigit;
31469 unsigned int v;
31470 int i;
31471 int n;
31472
31473 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
31474
31475 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
31476
31477 // Special case leading :: (e.g. "::1") to simplify processing later.
31478
31479 if( *inStr == ':' )
31480 {
31481 ++inStr;
31482 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
31483 }
31484
31485 // Parse the address.
31486
31487 ptr = inStr;
31488 dst = inBuffer;
31489 lim = dst + 16;
31490 colonPtr = NULL;
31491 sawDigit = 0;
31492 v = 0;
31493 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
31494 {
31495 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
31496 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
31497 {
31498 c -= '0';
31499 check( c < (int) countof( kASCIItoHexTable ) );
31500 v = ( v << 4 ) | kASCIItoHexTable[ c ];
31501 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
31502 sawDigit = 1;
31503 continue;
31504 }
31505 if( c == ':' )
31506 {
31507 ptr = inStr;
31508 if( !sawDigit )
31509 {
31510 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
31511 colonPtr = dst;
31512 continue;
31513 }
31514 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
31515 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
31516 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
31517 *dst++ = (uint8_t)( v & 0xFF );
31518 sawDigit = 0;
31519 v = 0;
31520 continue;
31521 }
31522
31523 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
31524
31525 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
31526 {
31527 err = _ParseIPv4Address( ptr, dst, &inStr );
31528 require_noerr_quiet( err, exit );
31529 dst += 4;
31530 sawDigit = 0;
31531 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
31532 }
31533 break;
31534 }
31535 if( sawDigit )
31536 {
31537 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
31538 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
31539 *dst++ = (uint8_t)( v & 0xFF );
31540 }
31541 check( dst <= lim );
31542 if( colonPtr )
31543 {
31544 require_action_quiet( dst < lim, exit, err = kOverrunErr );
31545 n = (int)( dst - colonPtr );
31546 for( i = 1; i <= n; ++i )
31547 {
31548 lim[ -i ] = colonPtr[ n - i ];
31549 colonPtr[ n - i ] = 0;
31550 }
31551 dst = lim;
31552 }
31553 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
31554
31555 *outStr = inStr - 1;
31556 err = kNoErr;
31557
31558 exit:
31559 return( err );
31560 }
31561
31562 //===========================================================================================================================
31563 // _ParseIPv6Scope
31564 //
31565 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31566 //===========================================================================================================================
31567
31568 static OSStatus _ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
31569 {
31570 #if( TARGET_OS_POSIX )
31571 OSStatus err;
31572 char scopeStr[ 64 ];
31573 char * dst;
31574 char * lim;
31575 int c;
31576 uint32_t scope;
31577 const char * ptr;
31578
31579 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
31580
31581 dst = scopeStr;
31582 lim = dst + ( countof( scopeStr ) - 1 );
31583 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
31584 {
31585 *dst++ = *inStr++;
31586 }
31587 *dst = '\0';
31588 check( dst <= lim );
31589
31590 // First try to map as a name and if that fails, treat it as a numeric scope.
31591
31592 scope = if_nametoindex( scopeStr );
31593 if( scope == 0 )
31594 {
31595 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
31596 {
31597 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
31598 }
31599 require_action_quiet( c == '\0', exit, err = kMalformedErr );
31600 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
31601 }
31602
31603 *outScope = scope;
31604 *outStr = inStr;
31605 err = kNoErr;
31606
31607 exit:
31608 return( err );
31609 #else
31610 OSStatus err;
31611 uint32_t scope;
31612 const char * start;
31613 int c;
31614
31615 scope = 0;
31616 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
31617 {
31618 scope = ( scope * 10 ) + ( c - '0' );
31619 }
31620 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
31621
31622 *outScope = scope;
31623 *outStr = inStr;
31624 err = kNoErr;
31625
31626 exit:
31627 return( err );
31628 #endif
31629 }
31630
31631 //===========================================================================================================================
31632 // _StringToIPv6Address
31633 //
31634 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31635 //===========================================================================================================================
31636
31637 static OSStatus
31638 _StringToIPv6Address(
31639 const char * inStr,
31640 StringToIPAddressFlags inFlags,
31641 uint8_t outIPv6[ 16 ],
31642 uint32_t * outScope,
31643 int * outPort,
31644 int * outPrefix,
31645 const char ** outStr )
31646 {
31647 OSStatus err;
31648 uint8_t ipv6[ 16 ];
31649 int c;
31650 int hasScope;
31651 uint32_t scope;
31652 int hasPort;
31653 int port;
31654 int hasPrefix;
31655 int prefix;
31656 int hasBracket;
31657 int i;
31658
31659 require_action( inStr, exit, err = kParamErr );
31660
31661 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
31662
31663 // Parse the address-only part of the address (e.g. "1::1").
31664
31665 err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
31666 require_noerr_quiet( err, exit );
31667 c = *inStr;
31668
31669 // Parse the scope, port, or prefix length.
31670
31671 hasScope = 0;
31672 scope = 0;
31673 hasPort = 0;
31674 port = 0;
31675 hasPrefix = 0;
31676 prefix = 0;
31677 hasBracket = 0;
31678 for( ;; )
31679 {
31680 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
31681 {
31682 require_action_quiet( !hasScope, exit, err = kMalformedErr );
31683 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
31684 ++inStr;
31685 err = _ParseIPv6Scope( inStr, &scope, &inStr );
31686 require_noerr_quiet( err, exit );
31687 hasScope = 1;
31688 c = *inStr;
31689 }
31690 else if( c == ':' ) // Port (e.g. ":80")
31691 {
31692 require_action_quiet( !hasPort, exit, err = kMalformedErr );
31693 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
31694 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
31695 require_action_quiet( port <= 65535, exit, err = kRangeErr );
31696 hasPort = 1;
31697 }
31698 else if( c == '/' ) // Prefix Length (e.g. "/64")
31699 {
31700 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
31701 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
31702 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
31703 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
31704 hasPrefix = 1;
31705 }
31706 else if( c == ']' )
31707 {
31708 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
31709 hasBracket = 1;
31710 c = *( ++inStr );
31711 }
31712 else
31713 {
31714 break;
31715 }
31716 }
31717
31718 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
31719
31720 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
31721 if( outScope && hasScope ) *outScope = scope;
31722 if( outPort && hasPort ) *outPort = port;
31723 if( outPrefix && hasPrefix ) *outPrefix = prefix;
31724 if( outStr ) *outStr = inStr;
31725 err = kNoErr;
31726
31727 exit:
31728 return( err );
31729 }
31730
31731 //===========================================================================================================================
31732 // _StringArray_Free
31733 //
31734 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
31735 //===========================================================================================================================
31736
31737 static void _StringArray_Free( char **inArray, size_t inCount )
31738 {
31739 size_t i;
31740
31741 for( i = 0; i < inCount; ++i )
31742 {
31743 free( inArray[ i ] );
31744 }
31745 if( inCount > 0 ) free( inArray );
31746 }
31747
31748 //===========================================================================================================================
31749 // _ParseQuotedEscapedString
31750 //
31751 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
31752 //===========================================================================================================================
31753
31754 static Boolean
31755 _ParseQuotedEscapedString(
31756 const char * inSrc,
31757 const char * inEnd,
31758 const char * inDelimiters,
31759 char * inBuf,
31760 size_t inMaxLen,
31761 size_t * outCopiedLen,
31762 size_t * outTotalLen,
31763 const char ** outSrc )
31764 {
31765 const unsigned char * src;
31766 const unsigned char * end;
31767 unsigned char * dst;
31768 unsigned char * lim;
31769 unsigned char c;
31770 unsigned char c2;
31771 size_t totalLen;
31772 Boolean singleQuote;
31773 Boolean doubleQuote;
31774
31775 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
31776 src = (const unsigned char *) inSrc;
31777 end = (const unsigned char *) inEnd;
31778 dst = (unsigned char *) inBuf;
31779 lim = dst + inMaxLen;
31780 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
31781 if( src >= end ) return( false );
31782
31783 // Parse each argument from the string.
31784 //
31785 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
31786
31787 totalLen = 0;
31788 singleQuote = false;
31789 doubleQuote = false;
31790 while( src < end )
31791 {
31792 c = *src++;
31793 if( singleQuote )
31794 {
31795 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
31796
31797 if( c == '\'' )
31798 {
31799 singleQuote = false;
31800 continue;
31801 }
31802 }
31803 else if( doubleQuote )
31804 {
31805 // Double quotes protect everything except double quotes and backslashes. A backslash can be
31806 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
31807 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
31808 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
31809 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
31810
31811 if( c == '"' )
31812 {
31813 doubleQuote = false;
31814 continue;
31815 }
31816 else if( c == '\\' )
31817 {
31818 if( src < end )
31819 {
31820 c2 = *src;
31821 if( ( c2 == '"' ) || ( c2 == '\\' ) )
31822 {
31823 ++src;
31824 c = c2;
31825 }
31826 else if( c2 == '\n' )
31827 {
31828 ++src;
31829 continue;
31830 }
31831 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
31832 {
31833 ++src;
31834 c = c2;
31835 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
31836 {
31837 c = HexPairToByte( src );
31838 src += 2;
31839 }
31840 }
31841 else if( isoctal_safe( c2 ) )
31842 {
31843 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
31844 {
31845 c = OctalTripleToByte( src );
31846 src += 3;
31847 }
31848 }
31849 }
31850 }
31851 }
31852 else if( strchr( inDelimiters, c ) )
31853 {
31854 break;
31855 }
31856 else if( c == '\\' )
31857 {
31858 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
31859 // A backslash followed by a newline disappears completely.
31860 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
31861 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
31862
31863 if( src < end )
31864 {
31865 c = *src;
31866 if( c == '\n' )
31867 {
31868 ++src;
31869 continue;
31870 }
31871 else if( ( c == 'x' ) || ( c == 'X' ) )
31872 {
31873 ++src;
31874 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
31875 {
31876 c = HexPairToByte( src );
31877 src += 2;
31878 }
31879 }
31880 else if( isoctal_safe( c ) )
31881 {
31882 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
31883 {
31884 c = OctalTripleToByte( src );
31885 src += 3;
31886 }
31887 else
31888 {
31889 ++src;
31890 }
31891 }
31892 else
31893 {
31894 ++src;
31895 }
31896 }
31897 }
31898 else if( c == '\'' )
31899 {
31900 singleQuote = true;
31901 continue;
31902 }
31903 else if( c == '"' )
31904 {
31905 doubleQuote = true;
31906 continue;
31907 }
31908
31909 if( dst < lim )
31910 {
31911 if( inBuf ) *dst = c;
31912 ++dst;
31913 }
31914 ++totalLen;
31915 }
31916
31917 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
31918 if( outTotalLen ) *outTotalLen = totalLen;
31919 if( outSrc ) *outSrc = (const char *) src;
31920 return( true );
31921 }
31922
31923 //===========================================================================================================================
31924 // _ServerSocketOpenEx2
31925 //
31926 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
31927 //===========================================================================================================================
31928
31929 static OSStatus
31930 _ServerSocketOpenEx2(
31931 int inFamily,
31932 int inType,
31933 int inProtocol,
31934 const void * inAddr,
31935 int inPort,
31936 int * outPort,
31937 int inRcvBufSize,
31938 Boolean inNoPortReuse,
31939 SocketRef * outSock )
31940 {
31941 OSStatus err;
31942 int port;
31943 SocketRef sock;
31944 int name;
31945 int option;
31946 sockaddr_ip sip;
31947 socklen_t len;
31948
31949 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
31950
31951 sock = socket( inFamily, inType, inProtocol );
31952 err = map_socket_creation_errno( sock );
31953 require_noerr_quiet( err, exit );
31954
31955 #if( defined( SO_NOSIGPIPE ) )
31956 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
31957 #endif
31958
31959 err = SocketMakeNonBlocking( sock );
31960 require_noerr( err, exit );
31961
31962 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
31963 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
31964 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
31965 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
31966
31967 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
31968 check_noerr( err );
31969
31970 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
31971
31972 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
31973 {
31974 option = 1;
31975 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
31976 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
31977 err = map_socket_noerr_errno( sock, err );
31978 require_noerr( err, exit );
31979 }
31980
31981 if( inFamily == AF_INET )
31982 {
31983 // Bind to the port. If it fails, retry with a dynamic port.
31984
31985 memset( &sip.v4, 0, sizeof( sip.v4 ) );
31986 SIN_LEN_SET( &sip.v4 );
31987 sip.v4.sin_family = AF_INET;
31988 sip.v4.sin_port = htons( (uint16_t) port );
31989 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
31990 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
31991 err = map_socket_noerr_errno( sock, err );
31992 if( err && ( inPort < 0 ) )
31993 {
31994 sip.v4.sin_port = 0;
31995 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
31996 err = map_socket_noerr_errno( sock, err );
31997 }
31998 require_noerr( err, exit );
31999 }
32000 #if( defined( AF_INET6 ) )
32001 else if( inFamily == AF_INET6 )
32002 {
32003 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
32004
32005 option = 1;
32006 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
32007 err = map_socket_noerr_errno( sock, err );
32008 require_noerr( err, exit );
32009
32010 // Bind to the port. If it fails, retry with a dynamic port.
32011
32012 memset( &sip.v6, 0, sizeof( sip.v6 ) );
32013 SIN6_LEN_SET( &sip.v6 );
32014 sip.v6.sin6_family = AF_INET6;
32015 sip.v6.sin6_port = htons( (uint16_t) port );
32016 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
32017 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
32018 err = map_socket_noerr_errno( sock, err );
32019 if( err && ( inPort < 0 ) )
32020 {
32021 sip.v6.sin6_port = 0;
32022 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
32023 err = map_socket_noerr_errno( sock, err );
32024 }
32025 require_noerr( err, exit );
32026 }
32027 #endif
32028 else
32029 {
32030 dlogassert( "Unsupported family: %d", inFamily );
32031 err = kUnsupportedErr;
32032 goto exit;
32033 }
32034
32035 if( inType == SOCK_STREAM )
32036 {
32037 err = listen( sock, SOMAXCONN );
32038 err = map_socket_noerr_errno( sock, err );
32039 if( err )
32040 {
32041 err = listen( sock, 5 );
32042 err = map_socket_noerr_errno( sock, err );
32043 require_noerr( err, exit );
32044 }
32045 }
32046
32047 if( outPort )
32048 {
32049 len = (socklen_t) sizeof( sip );
32050 err = getsockname( sock, &sip.sa, &len );
32051 err = map_socket_noerr_errno( sock, err );
32052 require_noerr( err, exit );
32053
32054 *outPort = SockAddrGetPort( &sip );
32055 }
32056 *outSock = sock;
32057 sock = kInvalidSocketRef;
32058
32059 exit:
32060 ForgetSocket( &sock );
32061 return( err );
32062 }
32063
32064 //===========================================================================================================================
32065 // _memdup
32066 //
32067 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
32068 //===========================================================================================================================
32069
32070 static void * _memdup( const void *inPtr, size_t inLen )
32071 {
32072 void * mem;
32073
32074 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
32075 require( mem, exit );
32076 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
32077
32078 exit:
32079 return( mem );
32080 }
32081
32082 //===========================================================================================================================
32083 // _memicmp
32084 //
32085 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
32086 //===========================================================================================================================
32087
32088 static int _memicmp( const void *inP1, const void *inP2, size_t inLen )
32089 {
32090 const unsigned char * p1;
32091 const unsigned char * e1;
32092 const unsigned char * p2;
32093 int c1;
32094 int c2;
32095
32096 p1 = (const unsigned char *) inP1;
32097 e1 = p1 + inLen;
32098 p2 = (const unsigned char *) inP2;
32099 while( p1 < e1 )
32100 {
32101 c1 = *p1++;
32102 c2 = *p2++;
32103 c1 = tolower( c1 );
32104 c2 = tolower( c2 );
32105 if( c1 < c2 ) return( -1 );
32106 if( c1 > c2 ) return( 1 );
32107 }
32108 return( 0 );
32109 }
32110
32111 //===========================================================================================================================
32112 // _FNV1
32113 //
32114 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
32115 //===========================================================================================================================
32116
32117 static uint32_t _FNV1( const void *inData, size_t inSize )
32118 {
32119 const uint8_t * src = (const uint8_t *) inData;
32120 const uint8_t * const end = src + inSize;
32121 uint32_t hash;
32122
32123 hash = 0x811c9dc5U;
32124 while( src != end )
32125 {
32126 hash *= 0x01000193;
32127 hash ^= *src++;
32128 }
32129 return( hash );
32130 }