]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil.c
mDNSResponder-878.250.4.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil.c
1 /*
2 Copyright (c) 2016-2018 Apple Inc. All rights reserved.
3
4 dnssdutil is a command-line utility for testing the DNS-SD API.
5 */
6
7 #include <CoreUtils/CommonServices.h> // Include early.
8 #include <CoreUtils/AsyncConnection.h>
9 #include <CoreUtils/AtomicUtils.h>
10 #include <CoreUtils/CFUtils.h>
11 #include <CoreUtils/CommandLineUtils.h>
12 #include <CoreUtils/DataBufferUtils.h>
13 #include <CoreUtils/DebugServices.h>
14 #include <CoreUtils/HTTPUtils.h>
15 #include <CoreUtils/JSONUtils.h>
16 #include <CoreUtils/LogUtils.h>
17 #include <CoreUtils/MiscUtils.h>
18 #include <CoreUtils/NetUtils.h>
19 #include <CoreUtils/PrintFUtils.h>
20 #include <CoreUtils/RandomNumberUtils.h>
21 #include <CoreUtils/SoftLinking.h>
22 #include <CoreUtils/StringUtils.h>
23 #include <CoreUtils/TickUtils.h>
24 #include <CoreUtils/TimeUtils.h>
25 #include <dns_sd.h>
26 #include <dns_sd_private.h>
27
28 #include CF_RUNTIME_HEADER
29
30 #if( TARGET_OS_DARWIN )
31 #include <CFNetwork/CFHost.h>
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCPrivate.h>
34 #include <dnsinfo.h>
35 #include <libproc.h>
36 #include <netdb.h>
37 #include <pcap.h>
38 #include <spawn.h>
39 #include <sys/proc_info.h>
40 #endif
41
42 #if( TARGET_OS_POSIX )
43 #include <sys/resource.h>
44 #endif
45
46 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
47 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
48 #endif
49
50 //===========================================================================================================================
51 // Versioning
52 //===========================================================================================================================
53
54 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
55
56 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
57 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
58 #endif
59
60 //===========================================================================================================================
61 // DNS-SD
62 //===========================================================================================================================
63
64 // DNS-SD API flag descriptors
65
66 #define kDNSServiceFlagsDescriptors \
67 "\x00" "AutoTrigger\0" \
68 "\x01" "Add\0" \
69 "\x02" "Default\0" \
70 "\x03" "NoAutoRename\0" \
71 "\x04" "Shared\0" \
72 "\x05" "Unique\0" \
73 "\x06" "BrowseDomains\0" \
74 "\x07" "RegistrationDomains\0" \
75 "\x08" "LongLivedQuery\0" \
76 "\x09" "AllowRemoteQuery\0" \
77 "\x0A" "ForceMulticast\0" \
78 "\x0B" "KnownUnique\0" \
79 "\x0C" "ReturnIntermediates\0" \
80 "\x0D" "NonBrowsable\0" \
81 "\x0E" "ShareConnection\0" \
82 "\x0F" "SuppressUnusable\0" \
83 "\x10" "Timeout\0" \
84 "\x11" "IncludeP2P\0" \
85 "\x12" "WakeOnResolve\0" \
86 "\x13" "BackgroundTrafficClass\0" \
87 "\x14" "IncludeAWDL\0" \
88 "\x15" "Validate\0" \
89 "\x16" "UnicastResponse\0" \
90 "\x17" "ValidateOptional\0" \
91 "\x18" "WakeOnlyService\0" \
92 "\x19" "ThresholdOne\0" \
93 "\x1A" "ThresholdFinder\0" \
94 "\x1B" "DenyCellular\0" \
95 "\x1C" "ServiceIndex\0" \
96 "\x1D" "DenyExpensive\0" \
97 "\x1E" "PathEvaluationDone\0" \
98 "\x1F" "AllowExpiredAnswers\0" \
99 "\x00"
100
101 #define kDNSServiceProtocolDescriptors \
102 "\x00" "IPv4\0" \
103 "\x01" "IPv6\0" \
104 "\x04" "UDP\0" \
105 "\x05" "TCP\0" \
106 "\x00"
107
108 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
109
110 //===========================================================================================================================
111 // DNS
112 //===========================================================================================================================
113
114 #define kDNSPort 53
115 #define kDNSMaxUDPMessageSize 512
116 #define kDNSMaxTCPMessageSize UINT16_MAX
117
118 #define kDomainLabelLengthMax 63
119 #define kDomainNameLengthMax 256
120
121 #define kDNSRecordDataLengthMax UINT16_MAX
122
123 typedef struct
124 {
125 uint8_t id[ 2 ];
126 uint8_t flags[ 2 ];
127 uint8_t questionCount[ 2 ];
128 uint8_t answerCount[ 2 ];
129 uint8_t authorityCount[ 2 ];
130 uint8_t additionalCount[ 2 ];
131
132 } DNSHeader;
133
134 #define kDNSHeaderLength 12
135 check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
136
137 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
138 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
139 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
140 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
141 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
142 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
143
144 #define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) )
145 #define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) )
146 #define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) )
147 #define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) )
148 #define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) )
149 #define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) )
150
151 // Single-bit DNS header fields
152
153 #define kDNSHeaderFlag_Response ( 1 << 15 ) // QR (bit 15), Query (0)/Response (1)
154 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) // AA (bit 10), Authoritative Answer
155 #define kDNSHeaderFlag_Truncation ( 1 << 9 ) // TC (bit 9), TrunCation
156 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) // RD (bit 8), Recursion Desired
157 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) // RA (bit 7), Recursion Available
158 #define kDNSHeaderFlag_Z ( 1 << 6 ) // Z (bit 6), Reserved (must be zero)
159 #define kDNSHeaderFlag_AuthenticData ( 1 << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6)
160 #define kDNSHeaderFlag_CheckingDisabled ( 1 << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6)
161
162 // OPCODE (bits 14-11), Operation Code
163
164 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
165 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
166 do{ (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
167
168 #define kDNSOpCode_Query 0 // QUERY (standard query)
169 #define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query)
170 #define kDNSOpCode_Status 2 // STATUS
171 #define kDNSOpCode_Notify 4 // NOTIFY
172 #define kDNSOpCode_Update 5 // UPDATE
173
174 // RCODE (bits 3-0), Response Code
175
176 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
177 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
178 do{ (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
179
180 #define kDNSRCode_NoError 0
181 #define kDNSRCode_FormatError 1
182 #define kDNSRCode_ServerFailure 2
183 #define kDNSRCode_NXDomain 3
184 #define kDNSRCode_NotImplemented 4
185 #define kDNSRCode_Refused 5
186
187 typedef struct
188 {
189 uint8_t type[ 2 ];
190 uint8_t class[ 2 ];
191
192 } DNSQuestionFixedFields;
193
194 check_compile_time( sizeof( DNSQuestionFixedFields ) == 4 );
195
196 #define DNSQuestionFixedFieldsInit( FIELDS, QTYPE, QCLASS ) \
197 do { WriteBig16( (FIELDS)->type, QTYPE ); WriteBig16( (FIELDS)->class, QCLASS ); } while( 0 )
198
199 #define DNSQuestionFixedFieldsGetType( FIELDS ) ReadBig16( (FIELDS)->type )
200 #define DNSQuestionFixedFieldsGetClass( FIELDS ) ReadBig16( (FIELDS)->class )
201
202 typedef struct
203 {
204 uint8_t type[ 2 ];
205 uint8_t class[ 2 ];
206 uint8_t ttl[ 4 ];
207 uint8_t rdlength[ 2 ];
208
209 } DNSRecordFixedFields;
210
211 check_compile_time( sizeof( DNSRecordFixedFields ) == 10 );
212
213 // SRV RDATA fixed-length fields. See <https://tools.ietf.org/html/rfc2782>.
214
215 typedef struct
216 {
217 uint8_t priority[ 2 ];
218 uint8_t weight[ 2 ];
219 uint8_t port[ 2 ];
220
221 } SRVRecordDataFixedFields;
222
223 check_compile_time( sizeof( SRVRecordDataFixedFields ) == 6 );
224
225 // SOA RDATA fixed-length fields. See <https://tools.ietf.org/html/rfc1035#section-3.3.13>.
226
227 typedef struct
228 {
229 uint8_t serial[ 4 ];
230 uint8_t refresh[ 4 ];
231 uint8_t retry[ 4 ];
232 uint8_t expire[ 4 ];
233 uint8_t minimum[ 4 ];
234
235 } SOARecordDataFixedFields;
236
237 check_compile_time( sizeof( SOARecordDataFixedFields ) == 20 );
238
239 // DNS message compression. See <https://tools.ietf.org/html/rfc1035#section-4.1.4>.
240
241 #define kDNSCompressionOffsetMax 0x3FFF
242
243 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
244 #define WriteDNSCompressionPtr( PTR, OFFSET ) \
245 do \
246 { \
247 ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( ( (OFFSET) >> 8 ) & 0x3F ) | 0xC0 ); \
248 ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( (OFFSET) & 0xFF ); \
249 \
250 } while( 0 )
251
252 #define NextLabel( LABEL ) ( ( (LABEL)[ 0 ] == 0 ) ? NULL : ( (LABEL) + 1 + (LABEL)[ 0 ] ) )
253
254 //===========================================================================================================================
255 // mDNS
256 //===========================================================================================================================
257
258 #define kMDNSPort 5353
259
260 #define kDefaultMDNSMessageID 0
261 #define kDefaultMDNSQueryFlags 0
262
263 #define kQClassUnicastResponseBit ( 1U << 15 )
264 #define kRRClassCacheFlushBit ( 1U << 15 )
265
266 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
267
268 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
269 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
270
271 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
272
273 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
274
275 #define kLocalStr "\x05" "local"
276 #define kLocalName ( (const uint8_t *) kLocalStr )
277 #define kLocalNameLen sizeof( kLocalStr )
278
279 //===========================================================================================================================
280 // Test Address Blocks
281 //===========================================================================================================================
282
283 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
284
285 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
286
287 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
288
289 static const uint8_t kDNSServerBaseAddrV6[] =
290 {
291 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
292 };
293
294 static const uint8_t kMDNSReplierBaseAddrV6[] =
295 {
296 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
297 };
298
299 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
300 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
301
302 // Bad IPv4 and IPv6 Address Blocks
303 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
304 // in 203.0.113.0/24 nor 2001:db8:1::/120.
305
306 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
307
308 static const uint8_t kDNSServerBadBaseAddrV6[] =
309 {
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
311 };
312
313 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
314
315 //===========================================================================================================================
316 // Misc.
317 //===========================================================================================================================
318
319 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
320 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
321
322 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
323
324 #if( !defined( strcpy_literal ) )
325 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
326 #endif
327
328 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
329 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
330
331 #define kNoSuchRecordStr "No Such Record"
332 #define kNoSuchRecordAStr "No Such Record (A)"
333 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
334
335 #define kRootLabel ( (const uint8_t *) "" )
336
337 //===========================================================================================================================
338 // Gerneral Command Options
339 //===========================================================================================================================
340
341 // Command option macros
342
343 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
344 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
345 (SHORT_HELP), NULL )
346
347 #define kRequiredOptionSuffix " [REQUIRED]"
348
349 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
350 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
351 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
352 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
353
354 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
355 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
356
357 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
358 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
359 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
360 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
361
362 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
363 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
364 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
365 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
366
367 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
368 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
369
370 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
371 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
372 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
373 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
374
375 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
376 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
377
378 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
379 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
380 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
381 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
382
383 // DNS-SD API flag options
384
385 static int gDNSSDFlags = 0;
386 static int gDNSSDFlag_AllowExpiredAnswers = false;
387 static int gDNSSDFlag_BrowseDomains = false;
388 static int gDNSSDFlag_DenyCellular = false;
389 static int gDNSSDFlag_DenyExpensive = false;
390 static int gDNSSDFlag_ForceMulticast = false;
391 static int gDNSSDFlag_IncludeAWDL = false;
392 static int gDNSSDFlag_NoAutoRename = false;
393 static int gDNSSDFlag_PathEvaluationDone = false;
394 static int gDNSSDFlag_RegistrationDomains = false;
395 static int gDNSSDFlag_ReturnIntermediates = false;
396 static int gDNSSDFlag_Shared = false;
397 static int gDNSSDFlag_SuppressUnusable = false;
398 static int gDNSSDFlag_Timeout = false;
399 static int gDNSSDFlag_UnicastResponse = false;
400 static int gDNSSDFlag_Unique = false;
401 static int gDNSSDFlag_WakeOnResolve = false;
402
403 #define DNSSDFlagsOption() \
404 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
405 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
406
407 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
408 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
409
410 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
411 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
412 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
413 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
414 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
415 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
416 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
417 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
418 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
419 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
420 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
421 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
422 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
423 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
424
425 // Interface option
426
427 static const char * gInterface = NULL;
428
429 #define InterfaceOption() \
430 StringOption( 'i', "interface", &gInterface, "interface", \
431 "Network interface by name or index. Use index -1 for local-only.", false )
432
433 // Connection options
434
435 #define kConnectionArg_Normal ""
436 #define kConnectionArgPrefix_PID "pid:"
437 #define kConnectionArgPrefix_UUID "uuid:"
438
439 static const char * gConnectionOpt = kConnectionArg_Normal;
440
441 #define ConnectionOptions() \
442 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
443 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
444 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
445
446 #define kConnectionSection_Name "Connection Option"
447 #define kConnectionSection_Text \
448 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
449 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
450 "specifying the connection option without an argument, i.e.,\n" \
451 "\n" \
452 " --connection\n" \
453 "\n" \
454 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
455 "\n" \
456 " --connection=pid:<PID>\n" \
457 "\n" \
458 "to specify the delegator by PID, or use\n" \
459 "\n" \
460 " --connection=uuid:<UUID>\n" \
461 "\n" \
462 "to specify the delegator by UUID.\n" \
463 "\n" \
464 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
465 "\n" \
466 " --no-connection\n"
467
468 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
469
470 // Help text for record data options
471
472 #define kRDataArgPrefix_Domain "domain:"
473 #define kRDataArgPrefix_File "file:"
474 #define kRDataArgPrefix_HexString "hex:"
475 #define kRDataArgPrefix_IPv4 "ipv4:"
476 #define kRDataArgPrefix_IPv6 "ipv6:"
477 #define kRDataArgPrefix_SRV "srv:"
478 #define kRDataArgPrefix_String "string:"
479 #define kRDataArgPrefix_TXT "txt:"
480
481 #define kRecordDataSection_Name "Record Data Arguments"
482 #define kRecordDataSection_Text \
483 "A record data argument is specified in one of the following formats:\n" \
484 "\n" \
485 "Format Syntax Example\n" \
486 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
487 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
488 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
489 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
490 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
491 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
492 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
493 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
494
495 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
496
497 //===========================================================================================================================
498 // Output Formatting
499 //===========================================================================================================================
500
501 #define kOutputFormatStr_JSON "json"
502 #define kOutputFormatStr_XML "xml"
503 #define kOutputFormatStr_Binary "binary"
504
505 typedef enum
506 {
507 kOutputFormatType_Invalid = 0,
508 kOutputFormatType_JSON = 1,
509 kOutputFormatType_XML = 2,
510 kOutputFormatType_Binary = 3
511
512 } OutputFormatType;
513
514 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
515 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
516 "\n" \
517 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
518 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
519 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
520 "\n" \
521 )
522
523 //===========================================================================================================================
524 // Browse Command Options
525 //===========================================================================================================================
526
527 static char ** gBrowse_ServiceTypes = NULL;
528 static size_t gBrowse_ServiceTypesCount = 0;
529 static const char * gBrowse_Domain = NULL;
530 static int gBrowse_DoResolve = false;
531 static int gBrowse_QueryTXT = false;
532 static int gBrowse_TimeLimitSecs = 0;
533
534 static CLIOption kBrowseOpts[] =
535 {
536 InterfaceOption(),
537 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
538 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
539
540 CLI_OPTION_GROUP( "Flags" ),
541 DNSSDFlagsOption(),
542 DNSSDFlagsOption_IncludeAWDL(),
543
544 CLI_OPTION_GROUP( "Operation" ),
545 ConnectionOptions(),
546 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
547 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
548 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
549
550 ConnectionSection(),
551 CLI_OPTION_END()
552 };
553
554 //===========================================================================================================================
555 // GetAddrInfo Command Options
556 //===========================================================================================================================
557
558 static const char * gGetAddrInfo_Name = NULL;
559 static int gGetAddrInfo_ProtocolIPv4 = false;
560 static int gGetAddrInfo_ProtocolIPv6 = false;
561 static int gGetAddrInfo_OneShot = false;
562 static int gGetAddrInfo_TimeLimitSecs = 0;
563
564 static CLIOption kGetAddrInfoOpts[] =
565 {
566 InterfaceOption(),
567 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
568 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
569 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
570
571 CLI_OPTION_GROUP( "Flags" ),
572 DNSSDFlagsOption(),
573 DNSSDFlagsOption_AllowExpiredAnswers(),
574 DNSSDFlagsOption_DenyCellular(),
575 DNSSDFlagsOption_DenyExpensive(),
576 DNSSDFlagsOption_PathEvalDone(),
577 DNSSDFlagsOption_ReturnIntermediates(),
578 DNSSDFlagsOption_SuppressUnusable(),
579 DNSSDFlagsOption_Timeout(),
580
581 CLI_OPTION_GROUP( "Operation" ),
582 ConnectionOptions(),
583 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
584 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
585
586 ConnectionSection(),
587 CLI_OPTION_END()
588 };
589
590 //===========================================================================================================================
591 // QueryRecord Command Options
592 //===========================================================================================================================
593
594 static const char * gQueryRecord_Name = NULL;
595 static const char * gQueryRecord_Type = NULL;
596 static int gQueryRecord_OneShot = false;
597 static int gQueryRecord_TimeLimitSecs = 0;
598 static int gQueryRecord_RawRData = false;
599
600 static CLIOption kQueryRecordOpts[] =
601 {
602 InterfaceOption(),
603 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
604 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
605
606 CLI_OPTION_GROUP( "Flags" ),
607 DNSSDFlagsOption(),
608 DNSSDFlagsOption_AllowExpiredAnswers(),
609 DNSSDFlagsOption_DenyCellular(),
610 DNSSDFlagsOption_DenyExpensive(),
611 DNSSDFlagsOption_ForceMulticast(),
612 DNSSDFlagsOption_IncludeAWDL(),
613 DNSSDFlagsOption_PathEvalDone(),
614 DNSSDFlagsOption_ReturnIntermediates(),
615 DNSSDFlagsOption_SuppressUnusable(),
616 DNSSDFlagsOption_Timeout(),
617 DNSSDFlagsOption_UnicastResponse(),
618
619 CLI_OPTION_GROUP( "Operation" ),
620 ConnectionOptions(),
621 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
622 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
623 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
624
625 ConnectionSection(),
626 CLI_OPTION_END()
627 };
628
629 //===========================================================================================================================
630 // Register Command Options
631 //===========================================================================================================================
632
633 static const char * gRegister_Name = NULL;
634 static const char * gRegister_Type = NULL;
635 static const char * gRegister_Domain = NULL;
636 static int gRegister_Port = 0;
637 static const char * gRegister_TXT = NULL;
638 static int gRegister_LifetimeMs = -1;
639 static const char ** gAddRecord_Types = NULL;
640 static size_t gAddRecord_TypesCount = 0;
641 static const char ** gAddRecord_Data = NULL;
642 static size_t gAddRecord_DataCount = 0;
643 static const char ** gAddRecord_TTLs = NULL;
644 static size_t gAddRecord_TTLsCount = 0;
645 static const char * gUpdateRecord_Data = NULL;
646 static int gUpdateRecord_DelayMs = 0;
647 static int gUpdateRecord_TTL = 0;
648
649 static CLIOption kRegisterOpts[] =
650 {
651 InterfaceOption(),
652 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
653 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
654 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
655 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
656 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
657
658 CLI_OPTION_GROUP( "Flags" ),
659 DNSSDFlagsOption(),
660 DNSSDFlagsOption_IncludeAWDL(),
661 DNSSDFlagsOption_NoAutoRename(),
662
663 CLI_OPTION_GROUP( "Operation" ),
664 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
665
666 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
667 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
668 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
669 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
670
671 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
672 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
673 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
674 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
675
676 RecordDataSection(),
677 CLI_OPTION_END()
678 };
679
680 //===========================================================================================================================
681 // RegisterRecord Command Options
682 //===========================================================================================================================
683
684 static const char * gRegisterRecord_Name = NULL;
685 static const char * gRegisterRecord_Type = NULL;
686 static const char * gRegisterRecord_Data = NULL;
687 static int gRegisterRecord_TTL = 0;
688 static int gRegisterRecord_LifetimeMs = -1;
689 static const char * gRegisterRecord_UpdateData = NULL;
690 static int gRegisterRecord_UpdateDelayMs = 0;
691 static int gRegisterRecord_UpdateTTL = 0;
692
693 static CLIOption kRegisterRecordOpts[] =
694 {
695 InterfaceOption(),
696 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
697 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
698 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
699 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
700
701 CLI_OPTION_GROUP( "Flags" ),
702 DNSSDFlagsOption(),
703 DNSSDFlagsOption_IncludeAWDL(),
704 DNSSDFlagsOption_Shared(),
705 DNSSDFlagsOption_Unique(),
706
707 CLI_OPTION_GROUP( "Operation" ),
708 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
709
710 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
711 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
712 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
713 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
714
715 RecordDataSection(),
716 CLI_OPTION_END()
717 };
718
719 //===========================================================================================================================
720 // Resolve Command Options
721 //===========================================================================================================================
722
723 static char * gResolve_Name = NULL;
724 static char * gResolve_Type = NULL;
725 static char * gResolve_Domain = NULL;
726 static int gResolve_TimeLimitSecs = 0;
727
728 static CLIOption kResolveOpts[] =
729 {
730 InterfaceOption(),
731 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
732 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
733 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
734
735 CLI_OPTION_GROUP( "Flags" ),
736 DNSSDFlagsOption(),
737 DNSSDFlagsOption_ForceMulticast(),
738 DNSSDFlagsOption_IncludeAWDL(),
739 DNSSDFlagsOption_ReturnIntermediates(),
740 DNSSDFlagsOption_WakeOnResolve(),
741
742 CLI_OPTION_GROUP( "Operation" ),
743 ConnectionOptions(),
744 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
745
746 ConnectionSection(),
747 CLI_OPTION_END()
748 };
749
750 //===========================================================================================================================
751 // Reconfirm Command Options
752 //===========================================================================================================================
753
754 static const char * gReconfirmRecord_Name = NULL;
755 static const char * gReconfirmRecord_Type = NULL;
756 static const char * gReconfirmRecord_Class = NULL;
757 static const char * gReconfirmRecord_Data = NULL;
758
759 static CLIOption kReconfirmOpts[] =
760 {
761 InterfaceOption(),
762 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
763 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
764 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
765 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
766
767 CLI_OPTION_GROUP( "Flags" ),
768 DNSSDFlagsOption(),
769
770 RecordDataSection(),
771 CLI_OPTION_END()
772 };
773
774 //===========================================================================================================================
775 // getaddrinfo-POSIX Command Options
776 //===========================================================================================================================
777
778 static const char * gGAIPOSIX_HostName = NULL;
779 static const char * gGAIPOSIX_ServName = NULL;
780 static const char * gGAIPOSIX_Family = NULL;
781 static int gGAIPOSIXFlag_AddrConfig = false;
782 static int gGAIPOSIXFlag_All = false;
783 static int gGAIPOSIXFlag_CanonName = false;
784 static int gGAIPOSIXFlag_NumericHost = false;
785 static int gGAIPOSIXFlag_NumericServ = false;
786 static int gGAIPOSIXFlag_Passive = false;
787 static int gGAIPOSIXFlag_V4Mapped = false;
788 #if( defined( AI_V4MAPPED_CFG ) )
789 static int gGAIPOSIXFlag_V4MappedCFG = false;
790 #endif
791 #if( defined( AI_DEFAULT ) )
792 static int gGAIPOSIXFlag_Default = false;
793 #endif
794 #if( defined( AI_UNUSABLE ) )
795 static int gGAIPOSIXFlag_Unusable = false;
796 #endif
797
798 static CLIOption kGetAddrInfoPOSIXOpts[] =
799 {
800 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
801 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
802
803 CLI_OPTION_GROUP( "Hints" ),
804 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
805 "\n"
806 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
807 "address family is specified, then AF_UNSPEC is used.\n"
808 "\n" ),
809 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
810 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
811 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
812 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
813 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
814 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
815 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
816 #if( defined( AI_V4MAPPED_CFG ) )
817 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
818 #endif
819 #if( defined( AI_DEFAULT ) )
820 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
821 #endif
822 #if( defined( AI_UNUSABLE ) )
823 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
824 #endif
825
826 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
827 CLI_OPTION_END()
828 };
829
830 //===========================================================================================================================
831 // ReverseLookup Command Options
832 //===========================================================================================================================
833
834 static const char * gReverseLookup_IPAddr = NULL;
835 static int gReverseLookup_OneShot = false;
836 static int gReverseLookup_TimeLimitSecs = 0;
837
838 static CLIOption kReverseLookupOpts[] =
839 {
840 InterfaceOption(),
841 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
842
843 CLI_OPTION_GROUP( "Flags" ),
844 DNSSDFlagsOption(),
845 DNSSDFlagsOption_ForceMulticast(),
846 DNSSDFlagsOption_ReturnIntermediates(),
847 DNSSDFlagsOption_SuppressUnusable(),
848
849 CLI_OPTION_GROUP( "Operation" ),
850 ConnectionOptions(),
851 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
852 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
853
854 ConnectionSection(),
855 CLI_OPTION_END()
856 };
857
858 //===========================================================================================================================
859 // PortMapping Command Options
860 //===========================================================================================================================
861
862 static int gPortMapping_ProtocolTCP = false;
863 static int gPortMapping_ProtocolUDP = false;
864 static int gPortMapping_InternalPort = 0;
865 static int gPortMapping_ExternalPort = 0;
866 static int gPortMapping_TTL = 0;
867
868 static CLIOption kPortMappingOpts[] =
869 {
870 InterfaceOption(),
871 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
872 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
873 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
874 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
875 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
876
877 CLI_OPTION_GROUP( "Flags" ),
878 DNSSDFlagsOption(),
879
880 CLI_OPTION_GROUP( "Operation" ),
881 ConnectionOptions(),
882
883 ConnectionSection(),
884 CLI_OPTION_END()
885 };
886
887 //===========================================================================================================================
888 // BrowseAll Command Options
889 //===========================================================================================================================
890
891 static const char * gBrowseAll_Domain = NULL;
892 static const char ** gBrowseAll_ServiceTypes = NULL;
893 static size_t gBrowseAll_ServiceTypesCount = 0;
894 static int gBrowseAll_BrowseTimeSecs = 5;
895 static int gBrowseAll_ConnectTimeout = 0;
896
897 static CLIOption kBrowseAllOpts[] =
898 {
899 InterfaceOption(),
900 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
901 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 ),
902
903 CLI_OPTION_GROUP( "Flags" ),
904 DNSSDFlagsOption_IncludeAWDL(),
905
906 CLI_OPTION_GROUP( "Operation" ),
907 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
908 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
909 CLI_OPTION_END()
910 };
911
912 //===========================================================================================================================
913 // GetNameInfo Command Options
914 //===========================================================================================================================
915
916 static void GetNameInfoCmd( void );
917
918 static char * gGetNameInfo_IPAddress = NULL;
919 static int gGetNameInfoFlag_DGram = false;
920 static int gGetNameInfoFlag_NameReqd = false;
921 static int gGetNameInfoFlag_NoFQDN = false;
922 static int gGetNameInfoFlag_NumericHost = false;
923 static int gGetNameInfoFlag_NumericScope = false;
924 static int gGetNameInfoFlag_NumericServ = false;
925
926 static CLIOption kGetNameInfoOpts[] =
927 {
928 StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
929
930 CLI_OPTION_GROUP( "Flags" ),
931 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ),
932 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ),
933 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ),
934 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ),
935 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
936 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ),
937
938 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
939 CLI_OPTION_END()
940 };
941
942 //===========================================================================================================================
943 // GetAddrInfoStress Command Options
944 //===========================================================================================================================
945
946 static int gGAIStress_TestDurationSecs = 0;
947 static int gGAIStress_ConnectionCount = 0;
948 static int gGAIStress_DurationMinMs = 0;
949 static int gGAIStress_DurationMaxMs = 0;
950 static int gGAIStress_RequestCountMax = 0;
951
952 static CLIOption kGetAddrInfoStressOpts[] =
953 {
954 InterfaceOption(),
955
956 CLI_OPTION_GROUP( "Flags" ),
957 DNSSDFlagsOption_ReturnIntermediates(),
958 DNSSDFlagsOption_SuppressUnusable(),
959
960 CLI_OPTION_GROUP( "Operation" ),
961 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
962 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
963 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
964 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
965 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
966 CLI_OPTION_END()
967 };
968
969 //===========================================================================================================================
970 // DNSQuery Command Options
971 //===========================================================================================================================
972
973 static char * gDNSQuery_Name = NULL;
974 static char * gDNSQuery_Type = "A";
975 static char * gDNSQuery_Server = NULL;
976 static int gDNSQuery_TimeLimitSecs = 5;
977 static int gDNSQuery_UseTCP = false;
978 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
979 static int gDNSQuery_RawRData = false;
980 static int gDNSQuery_Verbose = false;
981
982 #if( TARGET_OS_DARWIN )
983 #define kDNSQueryServerOptionIsRequired false
984 #else
985 #define kDNSQueryServerOptionIsRequired true
986 #endif
987
988 static CLIOption kDNSQueryOpts[] =
989 {
990 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
991 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
992 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
993 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
994 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
995 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
996 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
997 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
998 CLI_OPTION_END()
999 };
1000
1001 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1002 //===========================================================================================================================
1003 // DNSCrypt Command Options
1004 //===========================================================================================================================
1005
1006 static char * gDNSCrypt_ProviderName = NULL;
1007 static char * gDNSCrypt_ProviderKey = NULL;
1008 static char * gDNSCrypt_Name = NULL;
1009 static char * gDNSCrypt_Type = NULL;
1010 static char * gDNSCrypt_Server = NULL;
1011 static int gDNSCrypt_TimeLimitSecs = 5;
1012 static int gDNSCrypt_RawRData = false;
1013 static int gDNSCrypt_Verbose = false;
1014
1015 static CLIOption kDNSCryptOpts[] =
1016 {
1017 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
1018 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
1019 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
1020 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
1021 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
1022 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
1023 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
1024 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
1025 CLI_OPTION_END()
1026 };
1027 #endif
1028
1029 //===========================================================================================================================
1030 // MDNSQuery Command Options
1031 //===========================================================================================================================
1032
1033 static char * gMDNSQuery_Name = NULL;
1034 static char * gMDNSQuery_Type = NULL;
1035 static int gMDNSQuery_SourcePort = 0;
1036 static int gMDNSQuery_IsQU = false;
1037 static int gMDNSQuery_RawRData = false;
1038 static int gMDNSQuery_UseIPv4 = false;
1039 static int gMDNSQuery_UseIPv6 = false;
1040 static int gMDNSQuery_AllResponses = false;
1041 static int gMDNSQuery_ReceiveSecs = 1;
1042
1043 static CLIOption kMDNSQueryOpts[] =
1044 {
1045 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1046 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
1047 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
1048 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
1049 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
1050 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
1051 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
1052 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
1053 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
1054 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 ),
1055 CLI_OPTION_END()
1056 };
1057
1058 //===========================================================================================================================
1059 // MDNSCollider Command Options
1060 //===========================================================================================================================
1061
1062 #define kMDNSColliderProgramSection_Intro \
1063 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
1064 "ought to react to probe queries that match its record's name, if at all.\n" \
1065 "\n" \
1066 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
1067 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
1068 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
1069 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
1070 "accomplished with the following program:\n" \
1071 "\n" \
1072 " probes 3r; send; wait 5000\n" \
1073 "\n" \
1074 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
1075 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
1076 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
1077 "is more than enough time for the collider to respond to probe queries.\n" \
1078 "\n" \
1079 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
1080 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
1081 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1082
1083 #define kMDNSColliderProgramSection_Probes \
1084 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
1085 "\n" \
1086 "Usage: probes [<action-string>]\n" \
1087 "\n" \
1088 "The syntax for an action-string is\n" \
1089 "\n" \
1090 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
1091 " <action> ::= [<repeat-count>] <action-code>\n" \
1092 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
1093 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
1094 "\n" \
1095 "An expanded action-string is defined as\n" \
1096 "\n" \
1097 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
1098 "\n" \
1099 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
1100 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
1101 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
1102 "are not allowed.\n" \
1103 "\n" \
1104 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
1105 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
1106 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
1107 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
1108 "\n" \
1109 " Code Action\n" \
1110 " ---- ------\n" \
1111 " n Do nothing.\n" \
1112 " r Respond to the probe query.\n" \
1113 " u Respond to the probe query via unicast.\n" \
1114 " m Respond to the probe query via multicast.\n" \
1115 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1116 "\n" \
1117 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1118 "a response nor does it multicast its own probe query.\n"
1119
1120 #define kMDNSColliderProgramSection_Send \
1121 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1122 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1123 "\n" \
1124 "Usage: send\n"
1125
1126 #define kMDNSColliderProgramSection_Wait \
1127 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1128 "\n" \
1129 "Usage: wait <milliseconds>\n"
1130
1131 #define kMDNSColliderProgramSection_Loop \
1132 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1133 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1134 "\n" \
1135 "Usage: loop <non-zero count>; ... ; done\n" \
1136 "\n" \
1137 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1138 "\n" \
1139 " loop 3; wait 1000; send; done"
1140
1141 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1142
1143 static const char * gMDNSCollider_Name = NULL;
1144 static const char * gMDNSCollider_Type = NULL;
1145 static const char * gMDNSCollider_RecordData = NULL;
1146 static int gMDNSCollider_UseIPv4 = false;
1147 static int gMDNSCollider_UseIPv6 = false;
1148 static const char * gMDNSCollider_Program = NULL;
1149
1150 static CLIOption kMDNSColliderOpts[] =
1151 {
1152 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1153 StringOption( 'n', "name", &gMDNSCollider_Name, "name", "Collider's record name.", true ),
1154 StringOption( 't', "type", &gMDNSCollider_Type, "type", "Collider's record type.", true ),
1155 StringOption( 'd', "data", &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1156 StringOption( 'p', "program", &gMDNSCollider_Program, "program", "Program to execute. See Program section below.", true ),
1157 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4, "Use IPv4." ),
1158 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6, "Use IPv6." ),
1159
1160 RecordDataSection(),
1161 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro ),
1162 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes ),
1163 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send ),
1164 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait ),
1165 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop ),
1166 CLI_OPTION_END()
1167 };
1168
1169 static void MDNSColliderCmd( void );
1170
1171 //===========================================================================================================================
1172 // PIDToUUID Command Options
1173 //===========================================================================================================================
1174
1175 static int gPIDToUUID_PID = 0;
1176
1177 static CLIOption kPIDToUUIDOpts[] =
1178 {
1179 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1180 CLI_OPTION_END()
1181 };
1182
1183 //===========================================================================================================================
1184 // DNSServer Command Options
1185 //===========================================================================================================================
1186
1187 #define kDNSServerInfoText_Intro \
1188 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1189 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1190 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1191 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1192 "\n" \
1193 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1194
1195 #define kDNSServerInfoText_NameExistence \
1196 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1197 "\n" \
1198 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1199 "order, unless otherwise noted, consist of\n" \
1200 "\n" \
1201 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1202 " 2. at most one Count label;\n" \
1203 " 3. zero or more Tag labels;\n" \
1204 " 4. at most one TTL label; and\n" \
1205 " 5. at most one IPv4 or IPv6 label.\n" \
1206 "\n" \
1207 "An SRV name is defined as a name with the following form:\n" \
1208 "\n" \
1209 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1210 "\n" \
1211 "See \"SRV Names\" for details.\n"
1212
1213 #define kDNSServerInfoText_ResourceRecords \
1214 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1215 "\n" \
1216 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1217 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1218 "\n" \
1219 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1220 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1221 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1222 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1223 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1224 "\n" \
1225 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1226 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1227 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1228 "\n" \
1229 "SRV names are names of SRV records.\n" \
1230 "\n" \
1231 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1232 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1233 "AAAA records with specific TTL values.\n"
1234
1235 #define kDNSServerInfoText_AliasLabel \
1236 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1237 "\n" \
1238 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1239 "exactly N CNAME records:\n" \
1240 "\n" \
1241 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1242 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1243 " name has \"alias-(i - 1)\" as its first label.\n" \
1244 "\n" \
1245 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1246 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1247 " \"alias\" instead.\n" \
1248 "\n" \
1249 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1250 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1251 "\n" \
1252 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1253 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1254 "QNAME minus its first label.\n" \
1255 "\n" \
1256 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1257 "records:\n" \
1258 "\n" \
1259 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1260 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1261 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1262 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1263
1264 #define kDNSServerInfoText_AliasTTLLabel \
1265 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1266 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1267 "\n" \
1268 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1269 "will contain exactly N CNAME records:\n" \
1270 "\n" \
1271 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1272 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1273 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1274 "\n" \
1275 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1276 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1277 " label.\n" \
1278 "\n" \
1279 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1280 "CNAME records:\n" \
1281 "\n" \
1282 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1283 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1284 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1285
1286 #define kDNSServerInfoText_CountLabel \
1287 "Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [1, 255] and N_2 is\n" \
1288 "an integer in [N_1, 255].\n" \
1289 "\n" \
1290 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1291 "QTYPE, then the response will contain exactly N address records:\n" \
1292 "\n" \
1293 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1294 " and whose RDATA is an address equal to a constant base address + i.\n" \
1295 "\n" \
1296 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1297 "\n" \
1298 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1299 "records:\n" \
1300 "\n" \
1301 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1302 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1303 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1304 "\n" \
1305 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1306 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1307 "\n" \
1308 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1309 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1310 "\n" \
1311 " 2. The order of the address records will be random.\n" \
1312 "\n" \
1313 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1314 "following AAAA records:\n" \
1315 "\n" \
1316 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1317 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1318 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1319 "\n" \
1320 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1321 "will contain no address records, regardless of whether it contains a Count label.\n" \
1322 "\n" \
1323 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1324 "count-1\".\n"
1325
1326 #define kDNSServerInfoText_TagLabel \
1327 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1328 "\n" \
1329 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1330 "to increase the sizes of domain names.\n"
1331
1332 #define kDNSServerInfoText_TTLLabel \
1333 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1334 "\n" \
1335 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1336 "response will have a TTL value equal to T.\n"
1337
1338 #define kDNSServerInfoText_IPv4Label \
1339 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1340
1341 #define kDNSServerInfoText_IPv6Label \
1342 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1343
1344 #define kDNSServerInfoText_SRVNames \
1345 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1346 "\n" \
1347 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1348 "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" \
1349 "the target hostname of each of the SRV name's SRV records.\n" \
1350 "\n" \
1351 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1352 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1353 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1354 "d.test. labels, whichever comes first.\n" \
1355 "\n" \
1356 "Example. A response to an SRV record query with a QNAME of\n" \
1357 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1358 "\n" \
1359 "_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" \
1360 "_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"
1361
1362 #define kDNSServerInfoText_BadUDPMode \
1363 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1364 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1365 "that's not equal to the query's message ID.\n" \
1366 "\n" \
1367 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1368 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1369 "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" \
1370 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1371 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1372
1373 static int gDNSServer_LoopbackOnly = false;
1374 static int gDNSServer_Foreground = false;
1375 static int gDNSServer_ResponseDelayMs = 0;
1376 static int gDNSServer_DefaultTTL = 60;
1377 static int gDNSServer_Port = kDNSPort;
1378 static const char * gDNSServer_DomainOverride = NULL;
1379 #if( TARGET_OS_DARWIN )
1380 static const char * gDNSServer_FollowPID = NULL;
1381 #endif
1382 static int gDNSServer_BadUDPMode = false;
1383
1384 static CLIOption kDNSServerOpts[] =
1385 {
1386 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ),
1387 BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Direct log output to stdout instead of system logging." ),
1388 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1389 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1390 IntegerOption( 'p', "port", &gDNSServer_Port, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1391 StringOption( 0 , "domain", &gDNSServer_DomainOverride, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1392 #if( TARGET_OS_DARWIN )
1393 StringOption( 0 , "follow", &gDNSServer_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1394 #endif
1395 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1396
1397 CLI_SECTION( "Intro", kDNSServerInfoText_Intro ),
1398 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ),
1399 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ),
1400 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ),
1401 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ),
1402 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ),
1403 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ),
1404 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ),
1405 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ),
1406 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ),
1407 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames ),
1408 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode ),
1409 CLI_OPTION_END()
1410 };
1411
1412 static void DNSServerCmd( void );
1413
1414 //===========================================================================================================================
1415 // MDNSReplier Command Options
1416 //===========================================================================================================================
1417
1418 #define kMDNSReplierPortBase 50000
1419
1420 #define kMDNSReplierInfoText_Intro \
1421 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1422 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1423 "\n" \
1424 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1425
1426 #define kMDNSReplierInfoText_Parameters \
1427 "There are five parameters that control the replier's set of authoritative records.\n" \
1428 "\n" \
1429 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1430 " parameter is specified with the --hostname option.\n" \
1431 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1432 " option.\n" \
1433 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1434 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1435 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1436 " --maxInstanceCount option.\n" \
1437 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1438 " with the --countA option.\n" \
1439 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1440 " specified with the --countAAAA option.\n"
1441
1442 #define kMDNSReplierInfoText_PTR \
1443 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1444 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1445 "\n" \
1446 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1447 "\n" \
1448 " 1. The first PTR record is defined as\n" \
1449 "\n" \
1450 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1451 " TYPE: PTR\n" \
1452 " CLASS: IN\n" \
1453 " TTL: 4500\n" \
1454 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1455 "\n" \
1456 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1457 "\n" \
1458 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1459 " TYPE: PTR\n" \
1460 " CLASS: IN\n" \
1461 " TTL: 4500\n" \
1462 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1463
1464 #define kMDNSReplierInfoText_SRV \
1465 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1466 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1467 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1468 "\n" \
1469 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1470 "\n" \
1471 " 1. The first SRV record is defined as\n" \
1472 "\n" \
1473 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1474 " TYPE: SRV\n" \
1475 " CLASS: IN\n" \
1476 " TTL: 120\n" \
1477 " RDATA:\n" \
1478 " Priority: 0\n" \
1479 " Weight: 0\n" \
1480 " Port: (50000 + L) mod 2^16\n" \
1481 " Target: <hostname>.local.\n" \
1482 "\n" \
1483 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1484 "\n" \
1485 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1486 " TYPE: SRV\n" \
1487 " CLASS: IN\n" \
1488 " TTL: 120\n" \
1489 " RDATA:\n" \
1490 " Priority: 0\n" \
1491 " Weight: 0\n" \
1492 " Port: (50000 + L) mod 2^16\n" \
1493 " Target: <hostname>-<i>.local.\n"
1494
1495 #define kMDNSReplierInfoText_TXT \
1496 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1497 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1498 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1499 "\n" \
1500 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1501 "\n" \
1502 " 1. The first TXT record is defined as\n" \
1503 "\n" \
1504 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1505 " TYPE: TXT\n" \
1506 " CLASS: IN\n" \
1507 " TTL: 4500\n" \
1508 " RDLENGTH: L\n" \
1509 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1510 "\n" \
1511 " 2. For each i in [2, N], there is one TXT record:\n" \
1512 "\n" \
1513 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1514 " TYPE: TXT\n" \
1515 " CLASS: IN\n" \
1516 " TTL: 4500\n" \
1517 " RDLENGTH: L\n" \
1518 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1519 "\n" \
1520 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1521 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1522 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1523
1524 #define kMDNSReplierInfoText_A \
1525 "The replier has exactly N_max x N_a authoritative A records:\n" \
1526 "\n" \
1527 " 1. For each j in [1, N_a], an A record is defined as\n" \
1528 "\n" \
1529 " NAME: <hostname>.local.\n" \
1530 " TYPE: A\n" \
1531 " CLASS: IN\n" \
1532 " TTL: 120\n" \
1533 " RDLENGTH: 4\n" \
1534 " RDATA: 0.0.1.<j>\n" \
1535 "\n" \
1536 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1537 "\n" \
1538 " NAME: <hostname>-<i>.local.\n" \
1539 " TYPE: A\n" \
1540 " CLASS: IN\n" \
1541 " TTL: 120\n" \
1542 " RDLENGTH: 4\n" \
1543 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1544
1545 #define kMDNSReplierInfoText_AAAA \
1546 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1547 "\n" \
1548 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1549 "\n" \
1550 " NAME: <hostname>.local.\n" \
1551 " TYPE: AAAA\n" \
1552 " CLASS: IN\n" \
1553 " TTL: 120\n" \
1554 " RDLENGTH: 16\n" \
1555 " RDATA: 2001:db8:2::1:<j>\n" \
1556 "\n" \
1557 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1558 "\n" \
1559 " NAME: <hostname>-<i>.local.\n" \
1560 " TYPE: AAAA\n" \
1561 " CLASS: IN\n" \
1562 " TTL: 120\n" \
1563 " RDLENGTH: 16\n" \
1564 " RDATA: 2001:db8:2::<i>:<j>\n"
1565
1566 #define kMDNSReplierInfoText_Responses \
1567 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1568 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1569 "separate response messages.\n"
1570
1571 static const char * gMDNSReplier_Hostname = NULL;
1572 static const char * gMDNSReplier_ServiceTypeTag = NULL;
1573 static int gMDNSReplier_MaxInstanceCount = 1000;
1574 static int gMDNSReplier_NoAdditionals = false;
1575 static int gMDNSReplier_RecordCountA = 1;
1576 static int gMDNSReplier_RecordCountAAAA = 1;
1577 static double gMDNSReplier_UnicastDropRate = 0.0;
1578 static double gMDNSReplier_MulticastDropRate = 0.0;
1579 static int gMDNSReplier_MaxDropCount = 0;
1580 static int gMDNSReplier_UseIPv4 = false;
1581 static int gMDNSReplier_UseIPv6 = false;
1582 static int gMDNSReplier_Foreground = false;
1583 static const char * gMDNSReplier_FollowPID = NULL;
1584
1585 static CLIOption kMDNSReplierOpts[] =
1586 {
1587 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1588 StringOption( 'n', "hostname", &gMDNSReplier_Hostname, "string", "Base name to use for hostnames and service instance names.", true ),
1589 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1590 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount, "count", "Maximum number of service instances. (default: 1000)", false ),
1591 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals, "When answering queries, don't include any additional records." ),
1592 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1593 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1594 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1595 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1596 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1597 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4, "Use IPv4." ),
1598 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6, "Use IPv6." ),
1599 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground, "Direct log output to stdout instead of system logging." ),
1600 #if( TARGET_OS_DARWIN )
1601 StringOption( 0 , "follow", &gMDNSReplier_FollowPID, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1602 #endif
1603
1604 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro ),
1605 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters ),
1606 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR ),
1607 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV ),
1608 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT ),
1609 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A ),
1610 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA ),
1611 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses ),
1612 CLI_OPTION_END()
1613 };
1614
1615 static void MDNSReplierCmd( void );
1616
1617 //===========================================================================================================================
1618 // Test Command Options
1619 //===========================================================================================================================
1620
1621 #define kTestExitStatusSection_Name "Exit Status"
1622 #define kTestExitStatusSection_Text \
1623 "This test command can exit with one of three status codes:\n" \
1624 "\n" \
1625 "0 - The test ran to completion and passed.\n" \
1626 "1 - A fatal error prevented the test from completing.\n" \
1627 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1628 "\n" \
1629 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1630 "performance.\n"
1631
1632 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1633
1634 #define kGAIPerfTestSuiteName_Basic "basic"
1635 #define kGAIPerfTestSuiteName_Advanced "advanced"
1636
1637 static const char * gGAIPerf_TestSuite = NULL;
1638 static int gGAIPerf_CallDelayMs = 10;
1639 static int gGAIPerf_ServerDelayMs = 10;
1640 static int gGAIPerf_SkipPathEvalulation = false;
1641 static int gGAIPerf_BadUDPMode = false;
1642 static int gGAIPerf_IterationCount = 100;
1643 static const char * gGAIPerf_OutputFilePath = NULL;
1644 static const char * gGAIPerf_OutputFormat = kOutputFormatStr_JSON;
1645 static int gGAIPerf_OutputAppendNewline = false;
1646
1647 static void GAIPerfCmd( void );
1648
1649 #define kGAIPerfSectionText_TestSuiteBasic \
1650 "This test suite consists of the following three test cases:\n" \
1651 "\n" \
1652 "Test Case #1: Resolve a domain name with\n" \
1653 "\n" \
1654 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1655 "\n" \
1656 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1657 "server queries.\n" \
1658 "\n" \
1659 "Test Case #2: Resolve a domain name with\n" \
1660 "\n" \
1661 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1662 "\n" \
1663 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1664 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1665 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1666 "\n" \
1667 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1668 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1669 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1670 "\n" \
1671 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1672
1673 #define kGAIPerfSectionText_TestSuiteAdvanced \
1674 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1675 "\n" \
1676 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1677 "\n" \
1678 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1679 "\n" \
1680 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1681 "server queries.\n" \
1682 "\n" \
1683 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1684 "\n" \
1685 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1686 "\n" \
1687 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1688 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1689 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1690 "\n" \
1691 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1692 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1693 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1694 "\n" \
1695 "N_c and N_a take on the following values, depending on the value of N:\n" \
1696 "\n" \
1697 " N_c is 0 if N is in [1, 8].\n" \
1698 " N_c is 1 if N is in [9, 16].\n" \
1699 " N_c is 2 if N is in [17, 24].\n" \
1700 " N_c is 4 if N is in [25, 32].\n" \
1701 "\n" \
1702 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1703 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1704 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1705 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1706 "\n" \
1707 "Finally,\n" \
1708 "\n" \
1709 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1710
1711 static CLIOption kGAIPerfOpts[] =
1712 {
1713 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true,
1714 "\n"
1715 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1716 "\n"
1717 ),
1718 StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1719 FormatOption( 'f', "format", &gGAIPerf_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1720 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1721 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1722 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1723 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount, "count", "The number of iterations per test case. (default: 100)", false ),
1724
1725 CLI_OPTION_GROUP( "DNS Server Options" ),
1726 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1727 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1728
1729 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic ),
1730 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced ),
1731 TestExitStatusSection(),
1732 CLI_OPTION_END()
1733 };
1734
1735 static void MDNSDiscoveryTestCmd( void );
1736
1737 static int gMDNSDiscoveryTest_InstanceCount = 100;
1738 static int gMDNSDiscoveryTest_TXTSize = 100;
1739 static int gMDNSDiscoveryTest_BrowseTimeSecs = 2;
1740 static int gMDNSDiscoveryTest_FlushCache = false;
1741 static char * gMDNSDiscoveryTest_Interface = NULL;
1742 static int gMDNSDiscoveryTest_NoAdditionals = false;
1743 static int gMDNSDiscoveryTest_RecordCountA = 1;
1744 static int gMDNSDiscoveryTest_RecordCountAAAA = 1;
1745 static double gMDNSDiscoveryTest_UnicastDropRate = 0.0;
1746 static double gMDNSDiscoveryTest_MulticastDropRate = 0.0;
1747 static int gMDNSDiscoveryTest_MaxDropCount = 0;
1748 static int gMDNSDiscoveryTest_UseIPv4 = false;
1749 static int gMDNSDiscoveryTest_UseIPv6 = false;
1750 static const char * gMDNSDiscoveryTest_OutputFormat = kOutputFormatStr_JSON;
1751 static int gMDNSDiscoveryTest_OutputAppendNewline = false;
1752 static const char * gMDNSDiscoveryTest_OutputFilePath = NULL;
1753
1754 static CLIOption kMDNSDiscoveryTestOpts[] =
1755 {
1756 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount, "count", "Number of service instances to discover. (default: 100)", false ),
1757 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1758 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1759 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1760
1761 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1762 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1763 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals, "When answering queries, don't include any additional records." ),
1764 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA, "count", "Number of A records per hostname. (default: 1)", false ),
1765 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1766 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1767 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1768 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1769 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4, "Use IPv4." ),
1770 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6, "Use IPv6." ),
1771
1772 CLI_OPTION_GROUP( "Results" ),
1773 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1774 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1775 BooleanOption( 'n', "appendNewline", &gMDNSDiscoveryTest_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1776
1777 TestExitStatusSection(),
1778 CLI_OPTION_END()
1779 };
1780
1781 static void DotLocalTestCmd( void );
1782
1783 static const char * gDotLocalTest_Interface = NULL;
1784 static const char * gDotLocalTest_OutputFormat = kOutputFormatStr_JSON;
1785 static int gDotLocalTest_OutputAppendNewline = false;
1786 static const char * gDotLocalTest_OutputFilePath = NULL;
1787
1788 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1789 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1790 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1791 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1792 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1793 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1794 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1795
1796 #define kDotLocalTestSectionText_Description \
1797 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1798 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1799 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1800 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1801 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1802 "then the mdnsreplier's base hostname is test.local.\n" \
1803 "\n" \
1804 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1805 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1806 "\n" \
1807 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1808 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1809 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1810 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1811 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1812 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1813 "\n" \
1814 "Each subtest runs for five seconds.\n"
1815
1816 static CLIOption kDotLocalTestOpts[] =
1817 {
1818 StringOption( 'i', "interface", &gDotLocalTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1819
1820 CLI_OPTION_GROUP( "Results" ),
1821 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat, "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1822 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1823 BooleanOption( 'n', "appendNewline", &gDotLocalTest_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1824
1825 CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1826 TestExitStatusSection(),
1827 CLI_OPTION_END()
1828 };
1829
1830 static void ProbeConflictTestCmd( void );
1831
1832 static const char * gProbeConflictTest_Interface = NULL;
1833 static int gProbeConflictTest_UseComputerName = false;
1834 static const char * gProbeConflictTest_OutputFormat = kOutputFormatStr_JSON;
1835 static int gProbeConflictTest_OutputAppendNewline = false;
1836 static const char * gProbeConflictTest_OutputFilePath = NULL;
1837
1838 static CLIOption kProbeConflictTestOpts[] =
1839 {
1840 StringOption( 'i', "interface", &gProbeConflictTest_Interface, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1841 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1842
1843 CLI_OPTION_GROUP( "Results" ),
1844 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat, "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1845 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1846 BooleanOption( 'n', "appendNewline", &gProbeConflictTest_OutputAppendNewline, "If the output format is JSON, output a trailing newline character." ),
1847
1848 TestExitStatusSection(),
1849 CLI_OPTION_END()
1850 };
1851
1852 static CLIOption kTestOpts[] =
1853 {
1854 Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1855 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts, "Tests mDNS service discovery for correctness.", false ),
1856 Command( "dotlocal", DotLocalTestCmd, kDotLocalTestOpts, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1857 Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts, "Tests various probing conflict scenarios.", false ),
1858
1859 CLI_OPTION_END()
1860 };
1861
1862 //===========================================================================================================================
1863 // SSDP Command Options
1864 //===========================================================================================================================
1865
1866 static int gSSDPDiscover_MX = 1;
1867 static const char * gSSDPDiscover_ST = "ssdp:all";
1868 static int gSSDPDiscover_ReceiveSecs = 1;
1869 static int gSSDPDiscover_UseIPv4 = false;
1870 static int gSSDPDiscover_UseIPv6 = false;
1871 static int gSSDPDiscover_Verbose = false;
1872
1873 static CLIOption kSSDPDiscoverOpts[] =
1874 {
1875 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
1876 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1877 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1878 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1879 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1880 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1881 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
1882 CLI_OPTION_END()
1883 };
1884
1885 static void SSDPDiscoverCmd( void );
1886
1887 static CLIOption kSSDPOpts[] =
1888 {
1889 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
1890 CLI_OPTION_END()
1891 };
1892
1893 #if( TARGET_OS_DARWIN )
1894 //===========================================================================================================================
1895 // res_query Command Options
1896 //===========================================================================================================================
1897
1898 static void ResQueryCmd( void );
1899
1900 static const char * gResQuery_Name = NULL;
1901 static const char * gResQuery_Type = NULL;
1902 static const char * gResQuery_Class = NULL;
1903 static int gResQuery_UseLibInfo = false;
1904
1905 static CLIOption kResQueryOpts[] =
1906 {
1907 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
1908 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1909 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1910 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
1911 CLI_OPTION_END()
1912 };
1913
1914 //===========================================================================================================================
1915 // dns_query Command Options
1916 //===========================================================================================================================
1917
1918 static void ResolvDNSQueryCmd( void );
1919
1920 static const char * gResolvDNSQuery_Name = NULL;
1921 static const char * gResolvDNSQuery_Type = NULL;
1922 static const char * gResolvDNSQuery_Class = NULL;
1923 static const char * gResolvDNSQuery_Path = NULL;
1924
1925 static CLIOption kResolvDNSQueryOpts[] =
1926 {
1927 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
1928 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1929 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
1930 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1931 CLI_OPTION_END()
1932 };
1933
1934 //===========================================================================================================================
1935 // CFHost Command Options
1936 //===========================================================================================================================
1937
1938 static void CFHostCmd( void );
1939
1940 static const char * gCFHost_Name = NULL;
1941 static int gCFHost_WaitSecs = 0;
1942
1943 static CLIOption kCFHostOpts[] =
1944 {
1945 StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ),
1946 IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1947 CLI_OPTION_END()
1948 };
1949
1950 static CLIOption kLegacyOpts[] =
1951 {
1952 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1953 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
1954 Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ),
1955 CLI_OPTION_END()
1956 };
1957
1958 //===========================================================================================================================
1959 // DNSConfigAdd Command Options
1960 //===========================================================================================================================
1961
1962 static void DNSConfigAddCmd( void );
1963
1964 static CFStringRef gDNSConfigAdd_ID = NULL;
1965 static char ** gDNSConfigAdd_IPAddrArray = NULL;
1966 static size_t gDNSConfigAdd_IPAddrCount = 0;
1967 static char ** gDNSConfigAdd_DomainArray = NULL;
1968 static size_t gDNSConfigAdd_DomainCount = 0;
1969 static const char * gDNSConfigAdd_Interface = NULL;
1970
1971 static CLIOption kDNSConfigAddOpts[] =
1972 {
1973 CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ),
1974 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1975 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1976 StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ),
1977
1978 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1979 CLI_OPTION_END()
1980 };
1981
1982 //===========================================================================================================================
1983 // DNSConfigRemove Command Options
1984 //===========================================================================================================================
1985
1986 static void DNSConfigRemoveCmd( void );
1987
1988 static CFStringRef gDNSConfigRemove_ID = NULL;
1989
1990 static CLIOption kDNSConfigRemoveOpts[] =
1991 {
1992 CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
1993
1994 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1995 CLI_OPTION_END()
1996 };
1997
1998 static CLIOption kDNSConfigOpts[] =
1999 {
2000 Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2001 Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2002 CLI_OPTION_END()
2003 };
2004 #endif // TARGET_OS_DARWIN
2005
2006 //===========================================================================================================================
2007 // Command Table
2008 //===========================================================================================================================
2009
2010 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2011
2012 static void BrowseCmd( void );
2013 static void GetAddrInfoCmd( void );
2014 static void QueryRecordCmd( void );
2015 static void RegisterCmd( void );
2016 static void RegisterRecordCmd( void );
2017 static void ResolveCmd( void );
2018 static void ReconfirmCmd( void );
2019 static void GetAddrInfoPOSIXCmd( void );
2020 static void ReverseLookupCmd( void );
2021 static void PortMappingCmd( void );
2022 static void BrowseAllCmd( void );
2023 static void GetAddrInfoStressCmd( void );
2024 static void DNSQueryCmd( void );
2025 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2026 static void DNSCryptCmd( void );
2027 #endif
2028 static void MDNSQueryCmd( void );
2029 static void PIDToUUIDCmd( void );
2030 static void DaemonVersionCmd( void );
2031
2032 static CLIOption kGlobalOpts[] =
2033 {
2034 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2035 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2036 CLI_OPTION_HELP(),
2037
2038 // Common commands.
2039
2040 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2041 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2042 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2043 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
2044 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2045 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
2046 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2047 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2048 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2049 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2050 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2051
2052 // Uncommon commands.
2053
2054 Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ),
2055 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2056 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
2057 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2058 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
2059 #endif
2060 Command( "mdnsquery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
2061 Command( "mdnscollider", MDNSColliderCmd, kMDNSColliderOpts, "Creates record name collision scenarios.", true ),
2062 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
2063 Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ),
2064 Command( "mdnsreplier", MDNSReplierCmd, kMDNSReplierOpts, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2065 Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ),
2066 Command( "ssdp", NULL, kSSDPOpts, "Commands for testing Simple Service Discovery Protocol (SSDP).", true ),
2067 #if( TARGET_OS_DARWIN )
2068 Command( "legacy", NULL, kLegacyOpts, "Commands for legacy non-DNS-SD API.", true ),
2069 Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2070 #endif
2071 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
2072
2073 CLI_COMMAND_HELP(),
2074 CLI_OPTION_END()
2075 };
2076
2077 //===========================================================================================================================
2078 // Helper Prototypes
2079 //===========================================================================================================================
2080
2081 #define kExitReason_OneShotDone "one-shot done"
2082 #define kExitReason_ReceivedResponse "received response"
2083 #define kExitReason_SIGINT "interrupt signal"
2084 #define kExitReason_Timeout "timeout"
2085 #define kExitReason_TimeLimit "time limit"
2086
2087 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
2088
2089 static int
2090 PrintFTimestampHandler(
2091 PrintFContext * inContext,
2092 PrintFFormat * inFormat,
2093 PrintFVAList * inArgs,
2094 void * inUserContext );
2095 static int
2096 PrintFDNSMessageHandler(
2097 PrintFContext * inContext,
2098 PrintFFormat * inFormat,
2099 PrintFVAList * inArgs,
2100 void * inUserContext );
2101 static int
2102 PrintFAddRmvFlagsHandler(
2103 PrintFContext * inContext,
2104 PrintFFormat * inFormat,
2105 PrintFVAList * inArgs,
2106 void * inUserContext );
2107
2108 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
2109
2110 typedef enum
2111 {
2112 kConnectionType_None = 0,
2113 kConnectionType_Normal = 1,
2114 kConnectionType_DelegatePID = 2,
2115 kConnectionType_DelegateUUID = 3
2116
2117 } ConnectionType;
2118
2119 typedef struct
2120 {
2121 ConnectionType type;
2122 union
2123 {
2124 int32_t pid;
2125 uint8_t uuid[ 16 ];
2126
2127 } delegate;
2128
2129 } ConnectionDesc;
2130
2131 static OSStatus
2132 CreateConnectionFromArgString(
2133 const char * inString,
2134 dispatch_queue_t inQueue,
2135 DNSServiceRef * outSDRef,
2136 ConnectionDesc * outDesc );
2137 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2138 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2139 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2140 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
2141
2142 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2143
2144 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2145 static const char * RecordTypeToString( unsigned int inValue );
2146
2147 static OSStatus
2148 DNSMessageExtractDomainName(
2149 const uint8_t * inMsgPtr,
2150 size_t inMsgLen,
2151 const uint8_t * inNamePtr,
2152 uint8_t inBuf[ kDomainNameLengthMax ],
2153 const uint8_t ** outNextPtr );
2154 static OSStatus
2155 DNSMessageExtractDomainNameString(
2156 const void * inMsgPtr,
2157 size_t inMsgLen,
2158 const void * inNamePtr,
2159 char inBuf[ kDNSServiceMaxDomainName ],
2160 const uint8_t ** outNextPtr );
2161 static OSStatus
2162 DNSMessageExtractQuestion(
2163 const uint8_t * inMsgPtr,
2164 size_t inMsgLen,
2165 const uint8_t * inPtr,
2166 uint8_t inNameBuf[ kDomainNameLengthMax ],
2167 uint16_t * outType,
2168 uint16_t * outClass,
2169 const uint8_t ** outPtr );
2170 static OSStatus
2171 DNSMessageExtractRecord(
2172 const uint8_t * inMsgPtr,
2173 size_t inMsgLen,
2174 const uint8_t * inPtr,
2175 uint8_t inNameBuf[ kDomainNameLengthMax ],
2176 uint16_t * outType,
2177 uint16_t * outClass,
2178 uint32_t * outTTL,
2179 const uint8_t ** outRDataPtr,
2180 size_t * outRDataLen,
2181 const uint8_t ** outPtr );
2182 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr );
2183 static OSStatus
2184 DNSRecordDataToString(
2185 const void * inRDataPtr,
2186 size_t inRDataLen,
2187 unsigned int inRDataType,
2188 const void * inMsgPtr,
2189 size_t inMsgLen,
2190 char ** outString );
2191 static OSStatus
2192 DomainNameAppendString(
2193 uint8_t inDomainName[ kDomainNameLengthMax ],
2194 const char * inString,
2195 uint8_t ** outEndPtr );
2196 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
2197 static size_t DomainNameLength( const uint8_t *inName );
2198 static OSStatus DomainNameDupEx( const uint8_t *inName, Boolean inLower, uint8_t **outNamePtr, size_t *outNameLen );
2199 #define DomainNameDup( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, false, OUT_NAME, OUT_LEN )
2200 #define DomainNameDupLower( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, true, OUT_NAME, OUT_LEN )
2201
2202 static OSStatus
2203 DomainNameFromString(
2204 uint8_t inDomainName[ kDomainNameLengthMax ],
2205 const char * inString,
2206 uint8_t ** outEndPtr );
2207 static OSStatus
2208 DomainNameToString(
2209 const uint8_t * inDomainName,
2210 const uint8_t * inEnd,
2211 char inBuf[ kDNSServiceMaxDomainName ],
2212 const uint8_t ** outNextPtr );
2213
2214 static OSStatus
2215 DNSMessageToText(
2216 const uint8_t * inMsgPtr,
2217 size_t inMsgLen,
2218 Boolean inIsMDNS,
2219 Boolean inPrintRaw,
2220 char ** outText );
2221
2222 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
2223
2224 static OSStatus
2225 WriteDNSQueryMessage(
2226 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
2227 uint16_t inMsgID,
2228 uint16_t inFlags,
2229 const char * inQName,
2230 uint16_t inQType,
2231 uint16_t inQClass,
2232 size_t * outMsgLen );
2233
2234 // Dispatch helpers
2235
2236 typedef void ( *DispatchHandler )( void *inContext );
2237
2238 static OSStatus
2239 DispatchSignalSourceCreate(
2240 int inSignal,
2241 DispatchHandler inEventHandler,
2242 void * inContext,
2243 dispatch_source_t * outSource );
2244 static OSStatus
2245 DispatchSocketSourceCreate(
2246 SocketRef inSock,
2247 dispatch_source_type_t inType,
2248 dispatch_queue_t inQueue,
2249 DispatchHandler inEventHandler,
2250 DispatchHandler inCancelHandler,
2251 void * inContext,
2252 dispatch_source_t * outSource );
2253
2254 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2255 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2256
2257 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2258 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2259
2260 static OSStatus
2261 DispatchTimerCreate(
2262 dispatch_time_t inStart,
2263 uint64_t inIntervalNs,
2264 uint64_t inLeewayNs,
2265 dispatch_queue_t inQueue,
2266 DispatchHandler inEventHandler,
2267 DispatchHandler inCancelHandler,
2268 void * inContext,
2269 dispatch_source_t * outTimer );
2270
2271 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2272 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2273
2274 static OSStatus
2275 DispatchProcessMonitorCreate(
2276 pid_t inPID,
2277 unsigned long inFlags,
2278 dispatch_queue_t inQueue,
2279 DispatchHandler inEventHandler,
2280 DispatchHandler inCancelHandler,
2281 void * inContext,
2282 dispatch_source_t * outMonitor );
2283
2284 static const char * ServiceTypeDescription( const char *inName );
2285
2286 typedef struct
2287 {
2288 SocketRef sock; // Socket.
2289 void * userContext; // User context.
2290 int32_t refCount; // Reference count.
2291
2292 } SocketContext;
2293
2294 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
2295 static SocketContext * SocketContextRetain( SocketContext *inContext );
2296 static void SocketContextRelease( SocketContext *inContext );
2297 static void SocketContextCancelHandler( void *inContext );
2298
2299 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2300
2301 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
2302 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
2303 #if( TARGET_OS_DARWIN )
2304 static OSStatus StringToPID( const char *inString, pid_t *outPID );
2305 #endif
2306 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2307 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2308 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2309 #if( TARGET_OS_DARWIN )
2310 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
2311 #endif
2312 static OSStatus
2313 _ServerSocketOpenEx2(
2314 int inFamily,
2315 int inType,
2316 int inProtocol,
2317 const void * inAddr,
2318 int inPort,
2319 int * outPort,
2320 int inRcvBufSize,
2321 Boolean inNoPortReuse,
2322 SocketRef * outSock );
2323
2324 static const struct sockaddr * GetMDNSMulticastAddrV4( void );
2325 static const struct sockaddr * GetMDNSMulticastAddrV6( void );
2326 static OSStatus GetAnyMDNSInterface( char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2327
2328 static OSStatus
2329 CreateMulticastSocket(
2330 const struct sockaddr * inAddr,
2331 int inPort,
2332 const char * inIfName,
2333 uint32_t inIfIndex,
2334 Boolean inJoin,
2335 int * outPort,
2336 SocketRef * outSock );
2337
2338 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2339 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2340 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2341 static OSStatus CheckRootUser( void );
2342 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... );
2343 static OSStatus
2344 OutputPropertyList(
2345 CFPropertyListRef inPList,
2346 OutputFormatType inType,
2347 Boolean inAppendNewline,
2348 const char * inOutputFilePath );
2349 static void
2350 DNSRecordFixedFieldsSet(
2351 DNSRecordFixedFields * inFields,
2352 uint16_t inType,
2353 uint16_t inClass,
2354 uint32_t inTTL,
2355 uint16_t inRDLength );
2356 static void
2357 SRVRecordDataFixedFieldsGet(
2358 const SRVRecordDataFixedFields * inFields,
2359 unsigned int * outPriority,
2360 unsigned int * outWeight,
2361 unsigned int * outPort );
2362 static void
2363 SRVRecordDataFixedFieldsSet(
2364 SRVRecordDataFixedFields * inFields,
2365 uint16_t inPriority,
2366 uint16_t inWeight,
2367 uint16_t inPort );
2368 static void
2369 SOARecordDataFixedFieldsGet(
2370 const SOARecordDataFixedFields * inFields,
2371 uint32_t * outSerial,
2372 uint32_t * outRefresh,
2373 uint32_t * outRetry,
2374 uint32_t * outExpire,
2375 uint32_t * outMinimum );
2376 static void
2377 SOARecordDataFixedFieldsSet(
2378 SOARecordDataFixedFields * inFields,
2379 uint32_t inSerial,
2380 uint32_t inRefresh,
2381 uint32_t inRetry,
2382 uint32_t inExpire,
2383 uint32_t inMinimum );
2384 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2385 static OSStatus CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2386 static OSStatus
2387 CreateNSECRecordData(
2388 const uint8_t * inNextDomainName,
2389 uint8_t ** outPtr,
2390 size_t * outLen,
2391 unsigned int inTypeCount,
2392 ... );
2393 static OSStatus
2394 AppendSOARecord(
2395 DataBuffer * inDB,
2396 const uint8_t * inNamePtr,
2397 size_t inNameLen,
2398 uint16_t inType,
2399 uint16_t inClass,
2400 uint32_t inTTL,
2401 const uint8_t * inMName,
2402 const uint8_t * inRName,
2403 uint32_t inSerial,
2404 uint32_t inRefresh,
2405 uint32_t inRetry,
2406 uint32_t inExpire,
2407 uint32_t inMinimumTTL,
2408 size_t * outLen );
2409 static OSStatus
2410 CreateSOARecordData(
2411 const uint8_t * inMName,
2412 const uint8_t * inRName,
2413 uint32_t inSerial,
2414 uint32_t inRefresh,
2415 uint32_t inRetry,
2416 uint32_t inExpire,
2417 uint32_t inMinimumTTL,
2418 uint8_t ** outPtr,
2419 size_t * outLen );
2420 static OSStatus
2421 _DataBuffer_AppendDNSQuestion(
2422 DataBuffer * inDB,
2423 const uint8_t * inNamePtr,
2424 size_t inNameLen,
2425 uint16_t inType,
2426 uint16_t inClass );
2427 static OSStatus
2428 _DataBuffer_AppendDNSRecord(
2429 DataBuffer * inDB,
2430 const uint8_t * inNamePtr,
2431 size_t inNameLen,
2432 uint16_t inType,
2433 uint16_t inClass,
2434 uint32_t inTTL,
2435 const uint8_t * inRDataPtr,
2436 size_t inRDataLen );
2437 static char * _NanoTime64ToDateString( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2438
2439 #define Unused( X ) (void)(X)
2440
2441 //===========================================================================================================================
2442 // MDNSCollider
2443 //===========================================================================================================================
2444
2445 typedef struct MDNSColliderPrivate * MDNSColliderRef;
2446
2447 typedef uint32_t MDNSColliderProtocols;
2448 #define kMDNSColliderProtocol_None 0
2449 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2450 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2451
2452 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2453
2454 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2455 static OSStatus MDNSColliderStart( MDNSColliderRef inCollider );
2456 static void MDNSColliderStop( MDNSColliderRef inCollider );
2457 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2458 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2459 static OSStatus MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2460 static void
2461 MDNSColliderSetStopHandler(
2462 MDNSColliderRef inCollider,
2463 MDNSColliderStopHandler_f inStopHandler,
2464 void * inStopContext );
2465 static OSStatus
2466 MDNSColliderSetRecord(
2467 MDNSColliderRef inCollider,
2468 const uint8_t * inName,
2469 uint16_t inType,
2470 const void * inRDataPtr,
2471 size_t inRDataLen );
2472 static CFTypeID MDNSColliderGetTypeID( void );
2473
2474 //===========================================================================================================================
2475 // ServiceBrowser
2476 //===========================================================================================================================
2477
2478 typedef struct ServiceBrowserPrivate * ServiceBrowserRef;
2479 typedef struct ServiceBrowserResults ServiceBrowserResults;
2480 typedef struct SBRDomain SBRDomain;
2481 typedef struct SBRServiceType SBRServiceType;
2482 typedef struct SBRServiceInstance SBRServiceInstance;
2483 typedef struct SBRIPAddress SBRIPAddress;
2484
2485 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2486
2487 struct ServiceBrowserResults
2488 {
2489 SBRDomain * domainList; // List of domains in which services were found.
2490 };
2491
2492 struct SBRDomain
2493 {
2494 SBRDomain * next; // Next domain in list.
2495 char * name; // Name of domain represented by this object.
2496 SBRServiceType * typeList; // List of service types in this domain.
2497 };
2498
2499 struct SBRServiceType
2500 {
2501 SBRServiceType * next; // Next service type in list.
2502 char * name; // Name of service type represented by this object.
2503 SBRServiceInstance * instanceList; // List of service instances of this service type.
2504 };
2505
2506 struct SBRServiceInstance
2507 {
2508 SBRServiceInstance * next; // Next service instance in list.
2509 char * name; // Name of service instance represented by this object.
2510 char * hostname; // Target from service instance's SRV record.
2511 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
2512 uint16_t port; // Port from service instance's SRV record.
2513 uint8_t * txtPtr; // Service instance's TXT record data.
2514 size_t txtLen; // Service instance's TXT record data length.
2515 SBRIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
2516 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
2517 uint64_t resolveTimeUs; // Time it took to resolve this service instance in microseconds.
2518 };
2519
2520 struct SBRIPAddress
2521 {
2522 SBRIPAddress * next; // Next IP address in list.
2523 sockaddr_ip sip; // IPv4 or IPv6 address.
2524 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
2525 };
2526
2527 static CFTypeID ServiceBrowserGetTypeID( void );
2528 static OSStatus
2529 ServiceBrowserCreate(
2530 dispatch_queue_t inQueue,
2531 uint32_t inInterfaceIndex,
2532 const char * inDomain,
2533 unsigned int inBrowseTimeSecs,
2534 Boolean inIncludeAWDL,
2535 ServiceBrowserRef * outBrowser );
2536 static void ServiceBrowserStart( ServiceBrowserRef inBrowser );
2537 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2538 static void
2539 ServiceBrowserSetCallback(
2540 ServiceBrowserRef inBrowser,
2541 ServiceBrowserCallback_f inCallback,
2542 void * inContext );
2543 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2544 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2545
2546 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2547
2548 //===========================================================================================================================
2549 // main
2550 //===========================================================================================================================
2551
2552 int main( int argc, const char **argv )
2553 {
2554 OSStatus err;
2555
2556 // Route DebugServices logging output to stderr.
2557
2558 dlog_control( "DebugServices:output=file;stderr" );
2559
2560 PrintFRegisterExtension( "du:time", PrintFTimestampHandler, NULL );
2561 PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler, NULL );
2562 PrintFRegisterExtension( "du:arflags", PrintFAddRmvFlagsHandler, NULL );
2563 CLIInit( argc, argv );
2564 err = CLIParse( kGlobalOpts, kCLIFlags_None );
2565 if( err ) exit( 1 );
2566
2567 return( gExitCode );
2568 }
2569
2570 //===========================================================================================================================
2571 // VersionOptionCallback
2572 //===========================================================================================================================
2573
2574 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
2575 {
2576 const char * srcVers;
2577 #if( MDNSRESPONDER_PROJECT )
2578 char srcStr[ 16 ];
2579 #endif
2580
2581 Unused( inOption );
2582 Unused( inArg );
2583 Unused( inUnset );
2584
2585 #if( MDNSRESPONDER_PROJECT )
2586 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
2587 #else
2588 srcVers = DNSSDUTIL_SOURCE_VERSION;
2589 #endif
2590 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
2591
2592 return( kEndingErr );
2593 }
2594
2595 //===========================================================================================================================
2596 // BrowseCmd
2597 //===========================================================================================================================
2598
2599 typedef struct BrowseResolveOp BrowseResolveOp;
2600
2601 struct BrowseResolveOp
2602 {
2603 BrowseResolveOp * next; // Next resolve operation in list.
2604 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2605 char * fullName; // Full name of the service to resolve.
2606 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2607 };
2608
2609 typedef struct
2610 {
2611 DNSServiceRef mainRef; // Main sdRef for shared connection.
2612 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
2613 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
2614 const char * domain; // Domain for DNSServiceBrowse operation(s).
2615 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
2616 char ** serviceTypes; // Array of service types to browse for.
2617 size_t serviceTypesCount; // Count of array of service types to browse for.
2618 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
2619 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
2620 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
2621 Boolean printedHeader; // True if results header has been printed.
2622 Boolean doResolve; // True if service instances are to be resolved.
2623 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
2624
2625 } BrowseContext;
2626
2627 static void BrowsePrintPrologue( const BrowseContext *inContext );
2628 static void BrowseContextFree( BrowseContext *inContext );
2629 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
2630 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
2631 static void DNSSD_API
2632 BrowseCallback(
2633 DNSServiceRef inSDRef,
2634 DNSServiceFlags inFlags,
2635 uint32_t inInterfaceIndex,
2636 DNSServiceErrorType inError,
2637 const char * inName,
2638 const char * inRegType,
2639 const char * inDomain,
2640 void * inContext );
2641 static void DNSSD_API
2642 BrowseResolveCallback(
2643 DNSServiceRef inSDRef,
2644 DNSServiceFlags inFlags,
2645 uint32_t inInterfaceIndex,
2646 DNSServiceErrorType inError,
2647 const char * inFullName,
2648 const char * inHostname,
2649 uint16_t inPort,
2650 uint16_t inTXTLen,
2651 const unsigned char * inTXTPtr,
2652 void * inContext );
2653 static void DNSSD_API
2654 BrowseQueryRecordCallback(
2655 DNSServiceRef inSDRef,
2656 DNSServiceFlags inFlags,
2657 uint32_t inInterfaceIndex,
2658 DNSServiceErrorType inError,
2659 const char * inFullName,
2660 uint16_t inType,
2661 uint16_t inClass,
2662 uint16_t inRDataLen,
2663 const void * inRDataPtr,
2664 uint32_t inTTL,
2665 void * inContext );
2666
2667 static void BrowseCmd( void )
2668 {
2669 OSStatus err;
2670 size_t i;
2671 BrowseContext * context = NULL;
2672 dispatch_source_t signalSource = NULL;
2673 int useMainConnection;
2674
2675 // Set up SIGINT handler.
2676
2677 signal( SIGINT, SIG_IGN );
2678 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2679 require_noerr( err, exit );
2680 dispatch_resume( signalSource );
2681
2682 // Create context.
2683
2684 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
2685 require_action( context, exit, err = kNoMemoryErr );
2686
2687 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
2688 require_action( context->opRefs, exit, err = kNoMemoryErr );
2689 context->opRefsCount = gBrowse_ServiceTypesCount;
2690
2691 // Check command parameters.
2692
2693 if( gBrowse_TimeLimitSecs < 0 )
2694 {
2695 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
2696 err = kParamErr;
2697 goto exit;
2698 }
2699
2700 // Create main connection.
2701
2702 if( gConnectionOpt )
2703 {
2704 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2705 require_noerr_quiet( err, exit );
2706 useMainConnection = true;
2707 }
2708 else
2709 {
2710 useMainConnection = false;
2711 }
2712
2713 // Get flags.
2714
2715 context->flags = GetDNSSDFlagsFromOpts();
2716 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2717
2718 // Get interface.
2719
2720 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2721 require_noerr_quiet( err, exit );
2722
2723 // Set remaining parameters.
2724
2725 context->serviceTypes = gBrowse_ServiceTypes;
2726 context->serviceTypesCount = gBrowse_ServiceTypesCount;
2727 context->domain = gBrowse_Domain;
2728 context->doResolve = gBrowse_DoResolve ? true : false;
2729 context->timeLimitSecs = gBrowse_TimeLimitSecs;
2730 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
2731
2732 // Print prologue.
2733
2734 BrowsePrintPrologue( context );
2735
2736 // Start operation(s).
2737
2738 for( i = 0; i < context->serviceTypesCount; ++i )
2739 {
2740 DNSServiceRef sdRef;
2741
2742 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
2743 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
2744 BrowseCallback, context );
2745 require_noerr( err, exit );
2746
2747 context->opRefs[ i ] = sdRef;
2748 if( !useMainConnection )
2749 {
2750 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
2751 require_noerr( err, exit );
2752 }
2753 }
2754
2755 // Set time limit.
2756
2757 if( context->timeLimitSecs > 0 )
2758 {
2759 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2760 kExitReason_TimeLimit, Exit );
2761 }
2762 dispatch_main();
2763
2764 exit:
2765 dispatch_source_forget( &signalSource );
2766 if( context ) BrowseContextFree( context );
2767 if( err ) exit( 1 );
2768 }
2769
2770 //===========================================================================================================================
2771 // BrowsePrintPrologue
2772 //===========================================================================================================================
2773
2774 static void BrowsePrintPrologue( const BrowseContext *inContext )
2775 {
2776 const int timeLimitSecs = inContext->timeLimitSecs;
2777 const char * const * ptr = (const char **) inContext->serviceTypes;
2778 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
2779 char ifName[ kInterfaceNameBufLen ];
2780
2781 InterfaceIndexToName( inContext->ifIndex, ifName );
2782
2783 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2784 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2785 FPrintF( stdout, "Service types: %s", *ptr++ );
2786 while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ );
2787 FPrintF( stdout, "\n" );
2788 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2789 FPrintF( stdout, "Time limit: " );
2790 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2791 else FPrintF( stdout, "∞\n" );
2792 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
2793 FPrintF( stdout, "---\n" );
2794 }
2795
2796 //===========================================================================================================================
2797 // BrowseContextFree
2798 //===========================================================================================================================
2799
2800 static void BrowseContextFree( BrowseContext *inContext )
2801 {
2802 size_t i;
2803
2804 for( i = 0; i < inContext->opRefsCount; ++i )
2805 {
2806 DNSServiceForget( &inContext->opRefs[ i ] );
2807 }
2808 if( inContext->serviceTypes )
2809 {
2810 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
2811 inContext->serviceTypes = NULL;
2812 inContext->serviceTypesCount = 0;
2813 }
2814 DNSServiceForget( &inContext->mainRef );
2815 free( inContext );
2816 }
2817
2818 //===========================================================================================================================
2819 // BrowseResolveOpCreate
2820 //===========================================================================================================================
2821
2822 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
2823 {
2824 OSStatus err;
2825 BrowseResolveOp * resolveOp;
2826
2827 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
2828 require_action( resolveOp, exit, err = kNoMemoryErr );
2829
2830 resolveOp->fullName = strdup( inFullName );
2831 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
2832
2833 resolveOp->interfaceIndex = inInterfaceIndex;
2834
2835 *outOp = resolveOp;
2836 resolveOp = NULL;
2837 err = kNoErr;
2838
2839 exit:
2840 if( resolveOp ) BrowseResolveOpFree( resolveOp );
2841 return( err );
2842 }
2843
2844 //===========================================================================================================================
2845 // BrowseResolveOpFree
2846 //===========================================================================================================================
2847
2848 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
2849 {
2850 DNSServiceForget( &inOp->sdRef );
2851 ForgetMem( &inOp->fullName );
2852 free( inOp );
2853 }
2854
2855 //===========================================================================================================================
2856 // BrowseCallback
2857 //===========================================================================================================================
2858
2859 static void DNSSD_API
2860 BrowseCallback(
2861 DNSServiceRef inSDRef,
2862 DNSServiceFlags inFlags,
2863 uint32_t inInterfaceIndex,
2864 DNSServiceErrorType inError,
2865 const char * inName,
2866 const char * inRegType,
2867 const char * inDomain,
2868 void * inContext )
2869 {
2870 BrowseContext * const context = (BrowseContext *) inContext;
2871 OSStatus err;
2872 BrowseResolveOp * newOp = NULL;
2873 BrowseResolveOp ** p;
2874 char fullName[ kDNSServiceMaxDomainName ];
2875 struct timeval now;
2876
2877 Unused( inSDRef );
2878
2879 gettimeofday( &now, NULL );
2880
2881 err = inError;
2882 require_noerr( err, exit );
2883
2884 if( !context->printedHeader )
2885 {
2886 FPrintF( stdout, "%-26s %-14s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2887 context->printedHeader = true;
2888 }
2889 FPrintF( stdout, "%{du:time} %{du:arflags} %2d %-20s %-20s %s\n",
2890 &now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
2891
2892 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
2893
2894 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
2895 require_noerr( err, exit );
2896
2897 if( inFlags & kDNSServiceFlagsAdd )
2898 {
2899 DNSServiceRef sdRef;
2900 DNSServiceFlags flags;
2901
2902 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
2903 require_noerr( err, exit );
2904
2905 if( context->mainRef )
2906 {
2907 sdRef = context->mainRef;
2908 flags = kDNSServiceFlagsShareConnection;
2909 }
2910 else
2911 {
2912 flags = 0;
2913 }
2914 if( context->doResolve )
2915 {
2916 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
2917 NULL );
2918 require_noerr( err, exit );
2919 }
2920 else
2921 {
2922 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
2923 BrowseQueryRecordCallback, NULL );
2924 require_noerr( err, exit );
2925 }
2926
2927 newOp->sdRef = sdRef;
2928 if( !context->mainRef )
2929 {
2930 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
2931 require_noerr( err, exit );
2932 }
2933 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
2934 *p = newOp;
2935 newOp = NULL;
2936 }
2937 else
2938 {
2939 BrowseResolveOp * resolveOp;
2940
2941 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
2942 {
2943 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
2944 {
2945 break;
2946 }
2947 }
2948 if( resolveOp )
2949 {
2950 *p = resolveOp->next;
2951 BrowseResolveOpFree( resolveOp );
2952 }
2953 }
2954
2955 exit:
2956 if( newOp ) BrowseResolveOpFree( newOp );
2957 if( err ) exit( 1 );
2958 }
2959
2960 //===========================================================================================================================
2961 // BrowseQueryRecordCallback
2962 //===========================================================================================================================
2963
2964 static void DNSSD_API
2965 BrowseQueryRecordCallback(
2966 DNSServiceRef inSDRef,
2967 DNSServiceFlags inFlags,
2968 uint32_t inInterfaceIndex,
2969 DNSServiceErrorType inError,
2970 const char * inFullName,
2971 uint16_t inType,
2972 uint16_t inClass,
2973 uint16_t inRDataLen,
2974 const void * inRDataPtr,
2975 uint32_t inTTL,
2976 void * inContext )
2977 {
2978 OSStatus err;
2979 struct timeval now;
2980
2981 Unused( inSDRef );
2982 Unused( inClass );
2983 Unused( inTTL );
2984 Unused( inContext );
2985
2986 gettimeofday( &now, NULL );
2987
2988 err = inError;
2989 require_noerr( err, exit );
2990 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
2991
2992 FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2993 &now, ( inFlags & kDNSServiceFlagsAdd ) ? "Add" : "Rmv", inFullName, (int32_t) inInterfaceIndex,
2994 inRDataPtr, (size_t) inRDataLen );
2995
2996 exit:
2997 if( err ) exit( 1 );
2998 }
2999
3000 //===========================================================================================================================
3001 // BrowseResolveCallback
3002 //===========================================================================================================================
3003
3004 static void DNSSD_API
3005 BrowseResolveCallback(
3006 DNSServiceRef inSDRef,
3007 DNSServiceFlags inFlags,
3008 uint32_t inInterfaceIndex,
3009 DNSServiceErrorType inError,
3010 const char * inFullName,
3011 const char * inHostname,
3012 uint16_t inPort,
3013 uint16_t inTXTLen,
3014 const unsigned char * inTXTPtr,
3015 void * inContext )
3016 {
3017 struct timeval now;
3018 char errorStr[ 64 ];
3019
3020 Unused( inSDRef );
3021 Unused( inFlags );
3022 Unused( inContext );
3023
3024 gettimeofday( &now, NULL );
3025
3026 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3027
3028 FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3029 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3030 if( inTXTLen == 1 )
3031 {
3032 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3033 }
3034 else
3035 {
3036 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3037 }
3038 }
3039
3040 //===========================================================================================================================
3041 // GetAddrInfoCmd
3042 //===========================================================================================================================
3043
3044 typedef struct
3045 {
3046 DNSServiceRef mainRef; // Main sdRef for shared connection.
3047 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
3048 const char * name; // Hostname to resolve.
3049 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
3050 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
3051 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
3052 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3053 Boolean printedHeader; // True if the results header has been printed.
3054 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3055 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
3056 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
3057
3058 } GetAddrInfoContext;
3059
3060 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3061 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3062 static void DNSSD_API
3063 GetAddrInfoCallback(
3064 DNSServiceRef inSDRef,
3065 DNSServiceFlags inFlags,
3066 uint32_t inInterfaceIndex,
3067 DNSServiceErrorType inError,
3068 const char * inHostname,
3069 const struct sockaddr * inSockAddr,
3070 uint32_t inTTL,
3071 void * inContext );
3072
3073 static void GetAddrInfoCmd( void )
3074 {
3075 OSStatus err;
3076 DNSServiceRef sdRef;
3077 GetAddrInfoContext * context = NULL;
3078 dispatch_source_t signalSource = NULL;
3079 int useMainConnection;
3080
3081 // Set up SIGINT handler.
3082
3083 signal( SIGINT, SIG_IGN );
3084 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3085 require_noerr( err, exit );
3086 dispatch_resume( signalSource );
3087
3088 // Check command parameters.
3089
3090 if( gGetAddrInfo_TimeLimitSecs < 0 )
3091 {
3092 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3093 err = kParamErr;
3094 goto exit;
3095 }
3096
3097 // Create context.
3098
3099 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3100 require_action( context, exit, err = kNoMemoryErr );
3101
3102 // Create main connection.
3103
3104 if( gConnectionOpt )
3105 {
3106 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3107 require_noerr_quiet( err, exit );
3108 useMainConnection = true;
3109 }
3110 else
3111 {
3112 useMainConnection = false;
3113 }
3114
3115 // Get flags.
3116
3117 context->flags = GetDNSSDFlagsFromOpts();
3118 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3119
3120 // Get interface.
3121
3122 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3123 require_noerr_quiet( err, exit );
3124
3125 // Set remaining parameters.
3126
3127 context->name = gGetAddrInfo_Name;
3128 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
3129 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3130 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3131 if( gGetAddrInfo_OneShot )
3132 {
3133 context->oneShotMode = true;
3134 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3135 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3136 }
3137
3138 // Print prologue.
3139
3140 GetAddrInfoPrintPrologue( context );
3141
3142 // Start operation.
3143
3144 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3145 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3146 GetAddrInfoCallback, context );
3147 require_noerr( err, exit );
3148
3149 context->opRef = sdRef;
3150 if( !useMainConnection )
3151 {
3152 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3153 require_noerr( err, exit );
3154 }
3155
3156 // Set time limit.
3157
3158 if( context->timeLimitSecs > 0 )
3159 {
3160 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3161 kExitReason_TimeLimit, Exit );
3162 }
3163 dispatch_main();
3164
3165 exit:
3166 dispatch_source_forget( &signalSource );
3167 if( context ) GetAddrInfoContextFree( context );
3168 if( err ) exit( 1 );
3169 }
3170
3171 //===========================================================================================================================
3172 // GetAddrInfoPrintPrologue
3173 //===========================================================================================================================
3174
3175 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3176 {
3177 const int timeLimitSecs = inContext->timeLimitSecs;
3178 char ifName[ kInterfaceNameBufLen ];
3179
3180 InterfaceIndexToName( inContext->ifIndex, ifName );
3181
3182 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3183 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3184 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3185 FPrintF( stdout, "Name: %s\n", inContext->name );
3186 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3187 FPrintF( stdout, "Time limit: " );
3188 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3189 else FPrintF( stdout, "∞\n" );
3190 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3191 FPrintF( stdout, "---\n" );
3192 }
3193
3194 //===========================================================================================================================
3195 // GetAddrInfoContextFree
3196 //===========================================================================================================================
3197
3198 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3199 {
3200 DNSServiceForget( &inContext->opRef );
3201 DNSServiceForget( &inContext->mainRef );
3202 free( inContext );
3203 }
3204
3205 //===========================================================================================================================
3206 // GetAddrInfoCallback
3207 //===========================================================================================================================
3208
3209 static void DNSSD_API
3210 GetAddrInfoCallback(
3211 DNSServiceRef inSDRef,
3212 DNSServiceFlags inFlags,
3213 uint32_t inInterfaceIndex,
3214 DNSServiceErrorType inError,
3215 const char * inHostname,
3216 const struct sockaddr * inSockAddr,
3217 uint32_t inTTL,
3218 void * inContext )
3219 {
3220 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
3221 struct timeval now;
3222 OSStatus err;
3223 const char * addrStr;
3224 char addrStrBuf[ kSockAddrStringMaxSize ];
3225
3226 Unused( inSDRef );
3227
3228 gettimeofday( &now, NULL );
3229
3230 switch( inError )
3231 {
3232 case kDNSServiceErr_NoError:
3233 case kDNSServiceErr_NoSuchRecord:
3234 err = kNoErr;
3235 break;
3236
3237 case kDNSServiceErr_Timeout:
3238 Exit( kExitReason_Timeout );
3239
3240 default:
3241 err = inError;
3242 goto exit;
3243 }
3244
3245 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3246 {
3247 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3248 err = kTypeErr;
3249 goto exit;
3250 }
3251
3252 if( !inError )
3253 {
3254 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3255 require_noerr( err, exit );
3256 addrStr = addrStrBuf;
3257 }
3258 else
3259 {
3260 addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3261 }
3262
3263 if( !context->printedHeader )
3264 {
3265 FPrintF( stdout, "%-26s %-14s IF %-32s %-38s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3266 context->printedHeader = true;
3267 }
3268 FPrintF( stdout, "%{du:time} %{du:arflags} %2d %-32s %-38s %6u\n",
3269 &now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3270
3271 if( context->oneShotMode )
3272 {
3273 if( inFlags & kDNSServiceFlagsAdd )
3274 {
3275 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
3276 else context->needIPv6 = false;
3277 }
3278 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3279 {
3280 Exit( kExitReason_OneShotDone );
3281 }
3282 }
3283
3284 exit:
3285 if( err ) exit( 1 );
3286 }
3287
3288 //===========================================================================================================================
3289 // QueryRecordCmd
3290 //===========================================================================================================================
3291
3292 typedef struct
3293 {
3294 DNSServiceRef mainRef; // Main sdRef for shared connection.
3295 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
3296 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
3297 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
3298 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
3299 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3300 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
3301 Boolean printedHeader; // True if the results header was printed.
3302 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
3303 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
3304 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
3305
3306 } QueryRecordContext;
3307
3308 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3309 static void QueryRecordContextFree( QueryRecordContext *inContext );
3310 static void DNSSD_API
3311 QueryRecordCallback(
3312 DNSServiceRef inSDRef,
3313 DNSServiceFlags inFlags,
3314 uint32_t inInterfaceIndex,
3315 DNSServiceErrorType inError,
3316 const char * inFullName,
3317 uint16_t inType,
3318 uint16_t inClass,
3319 uint16_t inRDataLen,
3320 const void * inRDataPtr,
3321 uint32_t inTTL,
3322 void * inContext );
3323
3324 static void QueryRecordCmd( void )
3325 {
3326 OSStatus err;
3327 DNSServiceRef sdRef;
3328 QueryRecordContext * context = NULL;
3329 dispatch_source_t signalSource = NULL;
3330 int useMainConnection;
3331
3332 // Set up SIGINT handler.
3333
3334 signal( SIGINT, SIG_IGN );
3335 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3336 require_noerr( err, exit );
3337 dispatch_resume( signalSource );
3338
3339 // Create context.
3340
3341 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3342 require_action( context, exit, err = kNoMemoryErr );
3343
3344 // Check command parameters.
3345
3346 if( gQueryRecord_TimeLimitSecs < 0 )
3347 {
3348 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3349 err = kParamErr;
3350 goto exit;
3351 }
3352
3353 // Create main connection.
3354
3355 if( gConnectionOpt )
3356 {
3357 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3358 require_noerr_quiet( err, exit );
3359 useMainConnection = true;
3360 }
3361 else
3362 {
3363 useMainConnection = false;
3364 }
3365
3366 // Get flags.
3367
3368 context->flags = GetDNSSDFlagsFromOpts();
3369 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3370
3371 // Get interface.
3372
3373 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3374 require_noerr_quiet( err, exit );
3375
3376 // Get record type.
3377
3378 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3379 require_noerr( err, exit );
3380
3381 // Set remaining parameters.
3382
3383 context->recordName = gQueryRecord_Name;
3384 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
3385 context->oneShotMode = gQueryRecord_OneShot ? true : false;
3386 context->printRawRData = gQueryRecord_RawRData ? true : false;
3387
3388 // Print prologue.
3389
3390 QueryRecordPrintPrologue( context );
3391
3392 // Start operation.
3393
3394 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3395 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3396 kDNSServiceClass_IN, QueryRecordCallback, context );
3397 require_noerr( err, exit );
3398
3399 context->opRef = sdRef;
3400 if( !useMainConnection )
3401 {
3402 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3403 require_noerr( err, exit );
3404 }
3405
3406 // Set time limit.
3407
3408 if( context->timeLimitSecs > 0 )
3409 {
3410 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3411 Exit );
3412 }
3413 dispatch_main();
3414
3415 exit:
3416 dispatch_source_forget( &signalSource );
3417 if( context ) QueryRecordContextFree( context );
3418 if( err ) exit( 1 );
3419 }
3420
3421 //===========================================================================================================================
3422 // QueryRecordContextFree
3423 //===========================================================================================================================
3424
3425 static void QueryRecordContextFree( QueryRecordContext *inContext )
3426 {
3427 DNSServiceForget( &inContext->opRef );
3428 DNSServiceForget( &inContext->mainRef );
3429 free( inContext );
3430 }
3431
3432 //===========================================================================================================================
3433 // QueryRecordPrintPrologue
3434 //===========================================================================================================================
3435
3436 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3437 {
3438 const int timeLimitSecs = inContext->timeLimitSecs;
3439 char ifName[ kInterfaceNameBufLen ];
3440
3441 InterfaceIndexToName( inContext->ifIndex, ifName );
3442
3443 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3444 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3445 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3446 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3447 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
3448 FPrintF( stdout, "Time limit: " );
3449 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3450 else FPrintF( stdout, "∞\n" );
3451 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3452 FPrintF( stdout, "---\n" );
3453
3454 }
3455
3456 //===========================================================================================================================
3457 // QueryRecordCallback
3458 //===========================================================================================================================
3459
3460 static void DNSSD_API
3461 QueryRecordCallback(
3462 DNSServiceRef inSDRef,
3463 DNSServiceFlags inFlags,
3464 uint32_t inInterfaceIndex,
3465 DNSServiceErrorType inError,
3466 const char * inFullName,
3467 uint16_t inType,
3468 uint16_t inClass,
3469 uint16_t inRDataLen,
3470 const void * inRDataPtr,
3471 uint32_t inTTL,
3472 void * inContext )
3473 {
3474 QueryRecordContext * const context = (QueryRecordContext *) inContext;
3475 struct timeval now;
3476 OSStatus err;
3477 char * rdataStr = NULL;
3478
3479 Unused( inSDRef );
3480
3481 gettimeofday( &now, NULL );
3482
3483 switch( inError )
3484 {
3485 case kDNSServiceErr_NoError:
3486 case kDNSServiceErr_NoSuchRecord:
3487 err = kNoErr;
3488 break;
3489
3490 case kDNSServiceErr_Timeout:
3491 Exit( kExitReason_Timeout );
3492
3493 default:
3494 err = inError;
3495 goto exit;
3496 }
3497
3498 if( inError != kDNSServiceErr_NoSuchRecord )
3499 {
3500 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
3501 if( !rdataStr )
3502 {
3503 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
3504 require_action( rdataStr, exit, err = kNoMemoryErr );
3505 }
3506 }
3507
3508 if( !context->printedHeader )
3509 {
3510 FPrintF( stdout, "%-26s %-14s IF %-32s %-5s %-5s %6s RData\n",
3511 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3512 context->printedHeader = true;
3513 }
3514 FPrintF( stdout, "%{du:time} %{du:arflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3515 &now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
3516 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL,
3517 rdataStr ? rdataStr : kNoSuchRecordStr );
3518
3519 if( context->oneShotMode )
3520 {
3521 if( ( inFlags & kDNSServiceFlagsAdd ) &&
3522 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
3523 {
3524 context->gotRecord = true;
3525 }
3526 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
3527 }
3528
3529 exit:
3530 FreeNullSafe( rdataStr );
3531 if( err ) exit( 1 );
3532 }
3533
3534 //===========================================================================================================================
3535 // RegisterCmd
3536 //===========================================================================================================================
3537
3538 typedef struct
3539 {
3540 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
3541 uint8_t * dataPtr; // Record data.
3542 size_t dataLen; // Record data length.
3543 uint32_t ttl; // Record TTL value.
3544 uint16_t type; // Record type.
3545
3546 } ExtraRecord;
3547
3548 typedef struct
3549 {
3550 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
3551 const char * name; // Service name argument for DNSServiceRegister().
3552 const char * type; // Service type argument for DNSServiceRegister().
3553 const char * domain; // Domain in which advertise the service.
3554 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
3555 size_t txtLen; // Service TXT record data len.
3556 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
3557 size_t extraRecordsCount; // Number of extra records.
3558 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
3559 size_t updateTXTLen; // Length of record data for TXT record update.
3560 uint32_t updateTTL; // TTL of updated TXT record.
3561 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
3562 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
3563 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
3564 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3565 uint16_t port; // Service instance's port number.
3566 Boolean printedHeader; // True if results header was printed.
3567 Boolean didRegister; // True if service was registered.
3568
3569 } RegisterContext;
3570
3571 static void RegisterPrintPrologue( const RegisterContext *inContext );
3572 static void RegisterContextFree( RegisterContext *inContext );
3573 static void DNSSD_API
3574 RegisterCallback(
3575 DNSServiceRef inSDRef,
3576 DNSServiceFlags inFlags,
3577 DNSServiceErrorType inError,
3578 const char * inName,
3579 const char * inType,
3580 const char * inDomain,
3581 void * inContext );
3582 static void RegisterUpdate( void *inContext );
3583
3584 static void RegisterCmd( void )
3585 {
3586 OSStatus err;
3587 RegisterContext * context = NULL;
3588 dispatch_source_t signalSource = NULL;
3589
3590 // Set up SIGINT handler.
3591
3592 signal( SIGINT, SIG_IGN );
3593 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3594 require_noerr( err, exit );
3595 dispatch_resume( signalSource );
3596
3597 // Create context.
3598
3599 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
3600 require_action( context, exit, err = kNoMemoryErr );
3601
3602 // Check command parameters.
3603
3604 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
3605 {
3606 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
3607 err = kParamErr;
3608 goto exit;
3609 }
3610
3611 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
3612 {
3613 FPrintF( stderr, "There are missing additional record parameters.\n" );
3614 err = kParamErr;
3615 goto exit;
3616 }
3617
3618 // Get flags.
3619
3620 context->flags = GetDNSSDFlagsFromOpts();
3621
3622 // Get interface index.
3623
3624 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3625 require_noerr_quiet( err, exit );
3626
3627 // Get TXT record data.
3628
3629 if( gRegister_TXT )
3630 {
3631 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
3632 require_noerr_quiet( err, exit );
3633 }
3634
3635 // Set remaining parameters.
3636
3637 context->name = gRegister_Name;
3638 context->type = gRegister_Type;
3639 context->domain = gRegister_Domain;
3640 context->port = (uint16_t) gRegister_Port;
3641 context->lifetimeMs = gRegister_LifetimeMs;
3642
3643 if( gAddRecord_TypesCount > 0 )
3644 {
3645 size_t i;
3646
3647 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
3648 require_action( context, exit, err = kNoMemoryErr );
3649 context->extraRecordsCount = gAddRecord_TypesCount;
3650
3651 for( i = 0; i < gAddRecord_TypesCount; ++i )
3652 {
3653 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
3654
3655 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
3656 require_noerr( err, exit );
3657
3658 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
3659 if( err )
3660 {
3661 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
3662 err = kParamErr;
3663 goto exit;
3664 }
3665
3666 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
3667 require_noerr_quiet( err, exit );
3668 }
3669 }
3670
3671 if( gUpdateRecord_Data )
3672 {
3673 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
3674 require_noerr_quiet( err, exit );
3675
3676 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
3677 context->updateDelayMs = gUpdateRecord_DelayMs;
3678 }
3679
3680 // Print prologue.
3681
3682 RegisterPrintPrologue( context );
3683
3684 // Start operation.
3685
3686 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
3687 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
3688 RegisterCallback, context );
3689 ForgetMem( &context->txtPtr );
3690 require_noerr( err, exit );
3691
3692 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3693 require_noerr( err, exit );
3694
3695 dispatch_main();
3696
3697 exit:
3698 dispatch_source_forget( &signalSource );
3699 if( context ) RegisterContextFree( context );
3700 if( err ) exit( 1 );
3701 }
3702
3703 //===========================================================================================================================
3704 // RegisterPrintPrologue
3705 //===========================================================================================================================
3706
3707 static void RegisterPrintPrologue( const RegisterContext *inContext )
3708 {
3709 size_t i;
3710 int infinite;
3711 char ifName[ kInterfaceNameBufLen ];
3712
3713 InterfaceIndexToName( inContext->ifIndex, ifName );
3714
3715 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3716 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3717 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
3718 FPrintF( stdout, "Type: %s\n", inContext->type );
3719 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3720 FPrintF( stdout, "Port: %u\n", inContext->port );
3721 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
3722 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3723 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3724 if( inContext->updateTXTPtr )
3725 {
3726 FPrintF( stdout, "\nUpdate record:\n" );
3727 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
3728 FPrintF( stdout, " TTL: %u%?s\n",
3729 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
3730 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
3731 }
3732 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
3733 for( i = 0; i < inContext->extraRecordsCount; ++i )
3734 {
3735 const ExtraRecord * record = &inContext->extraRecords[ i ];
3736
3737 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
3738 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
3739 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
3740 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
3741 }
3742 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
3743 FPrintF( stdout, "---\n" );
3744 }
3745
3746 //===========================================================================================================================
3747 // RegisterContextFree
3748 //===========================================================================================================================
3749
3750 static void RegisterContextFree( RegisterContext *inContext )
3751 {
3752 ExtraRecord * record;
3753 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
3754
3755 DNSServiceForget( &inContext->opRef );
3756 ForgetMem( &inContext->txtPtr );
3757 for( record = inContext->extraRecords; record < end; ++record )
3758 {
3759 check( !record->recordRef );
3760 ForgetMem( &record->dataPtr );
3761 }
3762 ForgetMem( &inContext->extraRecords );
3763 ForgetMem( &inContext->updateTXTPtr );
3764 free( inContext );
3765 }
3766
3767 //===========================================================================================================================
3768 // RegisterCallback
3769 //===========================================================================================================================
3770
3771 static void DNSSD_API
3772 RegisterCallback(
3773 DNSServiceRef inSDRef,
3774 DNSServiceFlags inFlags,
3775 DNSServiceErrorType inError,
3776 const char * inName,
3777 const char * inType,
3778 const char * inDomain,
3779 void * inContext )
3780 {
3781 RegisterContext * const context = (RegisterContext *) inContext;
3782 OSStatus err;
3783 struct timeval now;
3784
3785 Unused( inSDRef );
3786
3787 gettimeofday( &now, NULL );
3788
3789 if( !context->printedHeader )
3790 {
3791 FPrintF( stdout, "%-26s %-14s Service\n", "Timestamp", "Flags" );
3792 context->printedHeader = true;
3793 }
3794 FPrintF( stdout, "%{du:time} %{du:arflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
3795
3796 require_noerr_action_quiet( inError, exit, err = inError );
3797
3798 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
3799 {
3800 context->didRegister = true;
3801 if( context->updateTXTPtr )
3802 {
3803 if( context->updateDelayMs > 0 )
3804 {
3805 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
3806 context, RegisterUpdate );
3807 }
3808 else
3809 {
3810 RegisterUpdate( context );
3811 }
3812 }
3813 if( context->extraRecordsCount > 0 )
3814 {
3815 ExtraRecord * record;
3816 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
3817
3818 for( record = context->extraRecords; record < end; ++record )
3819 {
3820 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
3821 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
3822 require_noerr( err, exit );
3823 }
3824 }
3825 if( context->lifetimeMs == 0 )
3826 {
3827 Exit( kExitReason_TimeLimit );
3828 }
3829 else if( context->lifetimeMs > 0 )
3830 {
3831 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
3832 kExitReason_TimeLimit, Exit );
3833 }
3834 }
3835 err = kNoErr;
3836
3837 exit:
3838 if( err ) exit( 1 );
3839 }
3840
3841 //===========================================================================================================================
3842 // RegisterUpdate
3843 //===========================================================================================================================
3844
3845 static void RegisterUpdate( void *inContext )
3846 {
3847 OSStatus err;
3848 RegisterContext * const context = (RegisterContext *) inContext;
3849
3850 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
3851 context->updateTTL );
3852 require_noerr( err, exit );
3853
3854 exit:
3855 if( err ) exit( 1 );
3856 }
3857
3858 //===========================================================================================================================
3859 // RegisterRecordCmd
3860 //===========================================================================================================================
3861
3862 typedef struct
3863 {
3864 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
3865 DNSRecordRef recordRef; // Registered record reference.
3866 const char * recordName; // Name of resource record.
3867 uint8_t * dataPtr; // Pointer to resource record data.
3868 size_t dataLen; // Length of resource record data.
3869 uint32_t ttl; // TTL value of resource record in seconds.
3870 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
3871 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
3872 int lifetimeMs; // Lifetime of the record registration in milliseconds.
3873 uint16_t recordType; // Resource record type.
3874 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
3875 size_t updateDataLen; // Length of data for record update.
3876 uint32_t updateTTL; // TTL for updated record.
3877 int updateDelayMs; // Post-registration record update delay in milliseconds.
3878 Boolean didRegister; // True if the record was registered.
3879
3880 } RegisterRecordContext;
3881
3882 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
3883 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
3884 static void DNSSD_API
3885 RegisterRecordCallback(
3886 DNSServiceRef inSDRef,
3887 DNSRecordRef inRecordRef,
3888 DNSServiceFlags inFlags,
3889 DNSServiceErrorType inError,
3890 void * inContext );
3891 static void RegisterRecordUpdate( void *inContext );
3892
3893 static void RegisterRecordCmd( void )
3894 {
3895 OSStatus err;
3896 RegisterRecordContext * context = NULL;
3897 dispatch_source_t signalSource = NULL;
3898
3899 // Set up SIGINT handler.
3900
3901 signal( SIGINT, SIG_IGN );
3902 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3903 require_noerr( err, exit );
3904 dispatch_resume( signalSource );
3905
3906 // Create context.
3907
3908 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
3909 require_action( context, exit, err = kNoMemoryErr );
3910
3911 // Create connection.
3912
3913 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
3914 require_noerr_quiet( err, exit );
3915
3916 // Get flags.
3917
3918 context->flags = GetDNSSDFlagsFromOpts();
3919
3920 // Get interface.
3921
3922 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3923 require_noerr_quiet( err, exit );
3924
3925 // Get record type.
3926
3927 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
3928 require_noerr( err, exit );
3929
3930 // Get record data.
3931
3932 if( gRegisterRecord_Data )
3933 {
3934 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
3935 require_noerr_quiet( err, exit );
3936 }
3937
3938 // Set remaining parameters.
3939
3940 context->recordName = gRegisterRecord_Name;
3941 context->ttl = (uint32_t) gRegisterRecord_TTL;
3942 context->lifetimeMs = gRegisterRecord_LifetimeMs;
3943
3944 // Get update data.
3945
3946 if( gRegisterRecord_UpdateData )
3947 {
3948 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
3949 require_noerr_quiet( err, exit );
3950
3951 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
3952 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
3953 }
3954
3955 // Print prologue.
3956
3957 RegisterRecordPrintPrologue( context );
3958
3959 // Start operation.
3960
3961 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
3962 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
3963 context->ttl, RegisterRecordCallback, context );
3964 if( err )
3965 {
3966 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
3967 goto exit;
3968 }
3969
3970 dispatch_main();
3971
3972 exit:
3973 dispatch_source_forget( &signalSource );
3974 if( context ) RegisterRecordContextFree( context );
3975 if( err ) exit( 1 );
3976 }
3977
3978 //===========================================================================================================================
3979 // RegisterRecordPrintPrologue
3980 //===========================================================================================================================
3981
3982 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
3983 {
3984 int infinite;
3985 char ifName[ kInterfaceNameBufLen ];
3986
3987 InterfaceIndexToName( inContext->ifIndex, ifName );
3988
3989 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3990 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3991 FPrintF( stdout, "Name: %s\n", inContext->recordName );
3992 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
3993 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
3994 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
3995 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
3996 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
3997 if( inContext->updateDataPtr )
3998 {
3999 FPrintF( stdout, "\nUpdate record:\n" );
4000 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
4001 FPrintF( stdout, " TTL: %u%?s\n",
4002 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4003 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
4004 }
4005 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4006 FPrintF( stdout, "---\n" );
4007 }
4008
4009 //===========================================================================================================================
4010 // RegisterRecordContextFree
4011 //===========================================================================================================================
4012
4013 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
4014 {
4015 DNSServiceForget( &inContext->conRef );
4016 ForgetMem( &inContext->dataPtr );
4017 ForgetMem( &inContext->updateDataPtr );
4018 free( inContext );
4019 }
4020
4021 //===========================================================================================================================
4022 // RegisterRecordCallback
4023 //===========================================================================================================================
4024
4025 static void
4026 RegisterRecordCallback(
4027 DNSServiceRef inSDRef,
4028 DNSRecordRef inRecordRef,
4029 DNSServiceFlags inFlags,
4030 DNSServiceErrorType inError,
4031 void * inContext )
4032 {
4033 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
4034 struct timeval now;
4035
4036 Unused( inSDRef );
4037 Unused( inRecordRef );
4038 Unused( inFlags );
4039 Unused( context );
4040
4041 gettimeofday( &now, NULL );
4042 FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4043
4044 if( !context->didRegister && !inError )
4045 {
4046 context->didRegister = true;
4047 if( context->updateDataPtr )
4048 {
4049 if( context->updateDelayMs > 0 )
4050 {
4051 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4052 context, RegisterRecordUpdate );
4053 }
4054 else
4055 {
4056 RegisterRecordUpdate( context );
4057 }
4058 }
4059 if( context->lifetimeMs == 0 )
4060 {
4061 Exit( kExitReason_TimeLimit );
4062 }
4063 else if( context->lifetimeMs > 0 )
4064 {
4065 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4066 kExitReason_TimeLimit, Exit );
4067 }
4068 }
4069 }
4070
4071 //===========================================================================================================================
4072 // RegisterRecordUpdate
4073 //===========================================================================================================================
4074
4075 static void RegisterRecordUpdate( void *inContext )
4076 {
4077 OSStatus err;
4078 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
4079
4080 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4081 context->updateDataPtr, context->updateTTL );
4082 require_noerr( err, exit );
4083
4084 exit:
4085 if( err ) exit( 1 );
4086 }
4087
4088 //===========================================================================================================================
4089 // ResolveCmd
4090 //===========================================================================================================================
4091
4092 typedef struct
4093 {
4094 DNSServiceRef mainRef; // Main sdRef for shared connections.
4095 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
4096 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
4097 const char * name; // Service name argument for DNSServiceResolve().
4098 const char * type; // Service type argument for DNSServiceResolve().
4099 const char * domain; // Domain argument for DNSServiceResolve().
4100 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
4101 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
4102
4103 } ResolveContext;
4104
4105 static void ResolvePrintPrologue( const ResolveContext *inContext );
4106 static void ResolveContextFree( ResolveContext *inContext );
4107 static void DNSSD_API
4108 ResolveCallback(
4109 DNSServiceRef inSDRef,
4110 DNSServiceFlags inFlags,
4111 uint32_t inInterfaceIndex,
4112 DNSServiceErrorType inError,
4113 const char * inFullName,
4114 const char * inHostname,
4115 uint16_t inPort,
4116 uint16_t inTXTLen,
4117 const unsigned char * inTXTPtr,
4118 void * inContext );
4119
4120 static void ResolveCmd( void )
4121 {
4122 OSStatus err;
4123 DNSServiceRef sdRef;
4124 ResolveContext * context = NULL;
4125 dispatch_source_t signalSource = NULL;
4126 int useMainConnection;
4127
4128 // Set up SIGINT handler.
4129
4130 signal( SIGINT, SIG_IGN );
4131 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4132 require_noerr( err, exit );
4133 dispatch_resume( signalSource );
4134
4135 // Create context.
4136
4137 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4138 require_action( context, exit, err = kNoMemoryErr );
4139
4140 // Check command parameters.
4141
4142 if( gResolve_TimeLimitSecs < 0 )
4143 {
4144 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4145 err = kParamErr;
4146 goto exit;
4147 }
4148
4149 // Create main connection.
4150
4151 if( gConnectionOpt )
4152 {
4153 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4154 require_noerr_quiet( err, exit );
4155 useMainConnection = true;
4156 }
4157 else
4158 {
4159 useMainConnection = false;
4160 }
4161
4162 // Get flags.
4163
4164 context->flags = GetDNSSDFlagsFromOpts();
4165 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4166
4167 // Get interface index.
4168
4169 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4170 require_noerr_quiet( err, exit );
4171
4172 // Set remaining parameters.
4173
4174 context->name = gResolve_Name;
4175 context->type = gResolve_Type;
4176 context->domain = gResolve_Domain;
4177 context->timeLimitSecs = gResolve_TimeLimitSecs;
4178
4179 // Print prologue.
4180
4181 ResolvePrintPrologue( context );
4182
4183 // Start operation.
4184
4185 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4186 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4187 ResolveCallback, NULL );
4188 require_noerr( err, exit );
4189
4190 context->opRef = sdRef;
4191 if( !useMainConnection )
4192 {
4193 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4194 require_noerr( err, exit );
4195 }
4196
4197 // Set time limit.
4198
4199 if( context->timeLimitSecs > 0 )
4200 {
4201 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4202 kExitReason_TimeLimit, Exit );
4203 }
4204 dispatch_main();
4205
4206 exit:
4207 dispatch_source_forget( &signalSource );
4208 if( context ) ResolveContextFree( context );
4209 if( err ) exit( 1 );
4210 }
4211
4212 //===========================================================================================================================
4213 // ReconfirmCmd
4214 //===========================================================================================================================
4215
4216 static void ReconfirmCmd( void )
4217 {
4218 OSStatus err;
4219 uint8_t * rdataPtr = NULL;
4220 size_t rdataLen = 0;
4221 DNSServiceFlags flags;
4222 uint32_t ifIndex;
4223 uint16_t type, class;
4224 char ifName[ kInterfaceNameBufLen ];
4225
4226 // Get flags.
4227
4228 flags = GetDNSSDFlagsFromOpts();
4229
4230 // Get interface index.
4231
4232 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4233 require_noerr_quiet( err, exit );
4234
4235 // Get record type.
4236
4237 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4238 require_noerr( err, exit );
4239
4240 // Get record data.
4241
4242 if( gReconfirmRecord_Data )
4243 {
4244 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4245 require_noerr_quiet( err, exit );
4246 }
4247
4248 // Get record class.
4249
4250 if( gReconfirmRecord_Class )
4251 {
4252 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4253 require_noerr( err, exit );
4254 }
4255 else
4256 {
4257 class = kDNSServiceClass_IN;
4258 }
4259
4260 // Print prologue.
4261
4262 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4263 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4264 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
4265 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
4266 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4267 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
4268 FPrintF( stdout, "---\n" );
4269
4270 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4271 FPrintF( stdout, "Error: %#m\n", err );
4272
4273 exit:
4274 FreeNullSafe( rdataPtr );
4275 if( err ) exit( 1 );
4276 }
4277
4278 //===========================================================================================================================
4279 // ResolvePrintPrologue
4280 //===========================================================================================================================
4281
4282 static void ResolvePrintPrologue( const ResolveContext *inContext )
4283 {
4284 const int timeLimitSecs = inContext->timeLimitSecs;
4285 char ifName[ kInterfaceNameBufLen ];
4286
4287 InterfaceIndexToName( inContext->ifIndex, ifName );
4288
4289 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4290 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4291 FPrintF( stdout, "Name: %s\n", inContext->name );
4292 FPrintF( stdout, "Type: %s\n", inContext->type );
4293 FPrintF( stdout, "Domain: %s\n", inContext->domain );
4294 FPrintF( stdout, "Time limit: " );
4295 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4296 else FPrintF( stdout, "∞\n" );
4297 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4298 FPrintF( stdout, "---\n" );
4299 }
4300
4301 //===========================================================================================================================
4302 // ResolveContextFree
4303 //===========================================================================================================================
4304
4305 static void ResolveContextFree( ResolveContext *inContext )
4306 {
4307 DNSServiceForget( &inContext->opRef );
4308 DNSServiceForget( &inContext->mainRef );
4309 free( inContext );
4310 }
4311
4312 //===========================================================================================================================
4313 // ResolveCallback
4314 //===========================================================================================================================
4315
4316 static void DNSSD_API
4317 ResolveCallback(
4318 DNSServiceRef inSDRef,
4319 DNSServiceFlags inFlags,
4320 uint32_t inInterfaceIndex,
4321 DNSServiceErrorType inError,
4322 const char * inFullName,
4323 const char * inHostname,
4324 uint16_t inPort,
4325 uint16_t inTXTLen,
4326 const unsigned char * inTXTPtr,
4327 void * inContext )
4328 {
4329 struct timeval now;
4330 char errorStr[ 64 ];
4331
4332 Unused( inSDRef );
4333 Unused( inFlags );
4334 Unused( inContext );
4335
4336 gettimeofday( &now, NULL );
4337
4338 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4339
4340 FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4341 &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4342 if( inTXTLen == 1 )
4343 {
4344 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4345 }
4346 else
4347 {
4348 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4349 }
4350 }
4351
4352 //===========================================================================================================================
4353 // GetAddrInfoPOSIXCmd
4354 //===========================================================================================================================
4355
4356 #define AddressFamilyStr( X ) ( \
4357 ( (X) == AF_INET ) ? "inet" : \
4358 ( (X) == AF_INET6 ) ? "inet6" : \
4359 ( (X) == AF_UNSPEC ) ? "unspec" : \
4360 "???" )
4361
4362 typedef struct
4363 {
4364 unsigned int flag;
4365 const char * str;
4366
4367 } FlagStringPair;
4368
4369 #define CaseFlagStringify( X ) { (X), # X }
4370
4371 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
4372 {
4373 #if( defined( AI_UNUSABLE ) )
4374 CaseFlagStringify( AI_UNUSABLE ),
4375 #endif
4376 CaseFlagStringify( AI_NUMERICSERV ),
4377 CaseFlagStringify( AI_V4MAPPED ),
4378 CaseFlagStringify( AI_ADDRCONFIG ),
4379 #if( defined( AI_V4MAPPED_CFG ) )
4380 CaseFlagStringify( AI_V4MAPPED_CFG ),
4381 #endif
4382 CaseFlagStringify( AI_ALL ),
4383 CaseFlagStringify( AI_NUMERICHOST ),
4384 CaseFlagStringify( AI_CANONNAME ),
4385 CaseFlagStringify( AI_PASSIVE ),
4386 { 0, NULL }
4387 };
4388
4389 static void GetAddrInfoPOSIXCmd( void )
4390 {
4391 OSStatus err;
4392 struct addrinfo hints;
4393 struct timeval now;
4394 const struct addrinfo * addrInfo;
4395 struct addrinfo * addrInfoList = NULL;
4396 const FlagStringPair * pair;
4397
4398 memset( &hints, 0, sizeof( hints ) );
4399 hints.ai_socktype = SOCK_STREAM;
4400
4401 // Set hints address family.
4402
4403 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
4404 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
4405 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
4406 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
4407 else
4408 {
4409 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4410 err = kParamErr;
4411 goto exit;
4412 }
4413
4414 // Set hints flags.
4415
4416 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
4417 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
4418 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
4419 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
4420 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
4421 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
4422 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
4423 #if( defined( AI_V4MAPPED_CFG ) )
4424 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
4425 #endif
4426 #if( defined( AI_DEFAULT ) )
4427 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
4428 #endif
4429 #if( defined( AI_UNUSABLE ) )
4430 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
4431 #endif
4432
4433 // Print prologue.
4434
4435 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
4436 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
4437 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
4438 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
4439 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4440 {
4441 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4442 }
4443 FPrintF( stdout, ">\n" );
4444 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4445 FPrintF( stdout, "---\n" );
4446
4447 // Call getaddrinfo().
4448
4449 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4450 gettimeofday( &now, NULL );
4451 if( err )
4452 {
4453 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4454 }
4455 else
4456 {
4457 int addrCount = 0;
4458
4459 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4460
4461 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4462 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4463 {
4464 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4465 }
4466 }
4467 FPrintF( stdout, "---\n" );
4468 FPrintF( stdout, "End time: %{du:time}\n", &now );
4469
4470 exit:
4471 if( addrInfoList ) freeaddrinfo( addrInfoList );
4472 if( err ) exit( 1 );
4473 }
4474
4475 //===========================================================================================================================
4476 // ReverseLookupCmd
4477 //===========================================================================================================================
4478
4479 #define kIP6ARPADomainStr "ip6.arpa."
4480
4481 static void ReverseLookupCmd( void )
4482 {
4483 OSStatus err;
4484 QueryRecordContext * context = NULL;
4485 DNSServiceRef sdRef;
4486 dispatch_source_t signalSource = NULL;
4487 uint32_t ipv4Addr;
4488 uint8_t ipv6Addr[ 16 ];
4489 char recordName[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr ) ];
4490 int useMainConnection;
4491 const char * endPtr;
4492
4493 // Set up SIGINT handler.
4494
4495 signal( SIGINT, SIG_IGN );
4496 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4497 require_noerr( err, exit );
4498 dispatch_resume( signalSource );
4499
4500 // Create context.
4501
4502 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
4503 require_action( context, exit, err = kNoMemoryErr );
4504
4505 // Check command parameters.
4506
4507 if( gReverseLookup_TimeLimitSecs < 0 )
4508 {
4509 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
4510 err = kParamErr;
4511 goto exit;
4512 }
4513
4514 // Create main connection.
4515
4516 if( gConnectionOpt )
4517 {
4518 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4519 require_noerr_quiet( err, exit );
4520 useMainConnection = true;
4521 }
4522 else
4523 {
4524 useMainConnection = false;
4525 }
4526
4527 // Get flags.
4528
4529 context->flags = GetDNSSDFlagsFromOpts();
4530 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4531
4532 // Get interface index.
4533
4534 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4535 require_noerr_quiet( err, exit );
4536
4537 // Create reverse lookup record name.
4538
4539 err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
4540 &ipv4Addr, NULL, NULL, NULL, &endPtr );
4541 if( err || ( *endPtr != '\0' ) )
4542 {
4543 char * dst;
4544 int i;
4545
4546 err = StringToIPv6Address( gReverseLookup_IPAddr,
4547 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
4548 ipv6Addr, NULL, NULL, NULL, &endPtr );
4549 if( err || ( *endPtr != '\0' ) )
4550 {
4551 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
4552 err = kParamErr;
4553 goto exit;
4554 }
4555 dst = recordName;
4556 for( i = 15; i >= 0; --i )
4557 {
4558 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
4559 *dst++ = '.';
4560 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
4561 *dst++ = '.';
4562 }
4563 strcpy_literal( dst, kIP6ARPADomainStr );
4564 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
4565 }
4566 else
4567 {
4568 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
4569 ipv4Addr & 0xFF,
4570 ( ipv4Addr >> 8 ) & 0xFF,
4571 ( ipv4Addr >> 16 ) & 0xFF,
4572 ( ipv4Addr >> 24 ) & 0xFF );
4573 }
4574
4575 // Set remaining parameters.
4576
4577 context->recordName = recordName;
4578 context->recordType = kDNSServiceType_PTR;
4579 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
4580 context->oneShotMode = gReverseLookup_OneShot ? true : false;
4581
4582 // Print prologue.
4583
4584 QueryRecordPrintPrologue( context );
4585
4586 // Start operation.
4587
4588 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4589 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
4590 kDNSServiceClass_IN, QueryRecordCallback, context );
4591 require_noerr( err, exit );
4592
4593 context->opRef = sdRef;
4594 if( !useMainConnection )
4595 {
4596 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4597 require_noerr( err, exit );
4598 }
4599
4600 // Set time limit.
4601
4602 if( context->timeLimitSecs > 0 )
4603 {
4604 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4605 kExitReason_TimeLimit, Exit );
4606 }
4607 dispatch_main();
4608
4609 exit:
4610 dispatch_source_forget( &signalSource );
4611 if( context ) QueryRecordContextFree( context );
4612 if( err ) exit( 1 );
4613 }
4614
4615 //===========================================================================================================================
4616 // PortMappingCmd
4617 //===========================================================================================================================
4618
4619 typedef struct
4620 {
4621 DNSServiceRef mainRef; // Main sdRef for shared connection.
4622 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
4623 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
4624 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4625 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4626 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
4627 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4628 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
4629 Boolean printedHeader; // True if results header was printed.
4630
4631 } PortMappingContext;
4632
4633 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
4634 static void PortMappingContextFree( PortMappingContext *inContext );
4635 static void DNSSD_API
4636 PortMappingCallback(
4637 DNSServiceRef inSDRef,
4638 DNSServiceFlags inFlags,
4639 uint32_t inInterfaceIndex,
4640 DNSServiceErrorType inError,
4641 uint32_t inExternalIPv4Address,
4642 DNSServiceProtocol inProtocol,
4643 uint16_t inInternalPort,
4644 uint16_t inExternalPort,
4645 uint32_t inTTL,
4646 void * inContext );
4647
4648 static void PortMappingCmd( void )
4649 {
4650 OSStatus err;
4651 PortMappingContext * context = NULL;
4652 DNSServiceRef sdRef;
4653 dispatch_source_t signalSource = NULL;
4654 int useMainConnection;
4655
4656 // Set up SIGINT handler.
4657
4658 signal( SIGINT, SIG_IGN );
4659 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
4660 require_noerr( err, exit );
4661 dispatch_resume( signalSource );
4662
4663 // Create context.
4664
4665 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
4666 require_action( context, exit, err = kNoMemoryErr );
4667
4668 // Check command parameters.
4669
4670 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
4671 {
4672 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
4673 err = kParamErr;
4674 goto exit;
4675 }
4676
4677 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
4678 {
4679 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
4680 err = kParamErr;
4681 goto exit;
4682 }
4683
4684 // Create main connection.
4685
4686 if( gConnectionOpt )
4687 {
4688 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4689 require_noerr_quiet( err, exit );
4690 useMainConnection = true;
4691 }
4692 else
4693 {
4694 useMainConnection = false;
4695 }
4696
4697 // Get flags.
4698
4699 context->flags = GetDNSSDFlagsFromOpts();
4700 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4701
4702 // Get interface index.
4703
4704 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4705 require_noerr_quiet( err, exit );
4706
4707 // Set remaining parameters.
4708
4709 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
4710 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
4711 context->ttl = (uint32_t) gPortMapping_TTL;
4712 context->internalPort = (uint16_t) gPortMapping_InternalPort;
4713 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
4714
4715 // Print prologue.
4716
4717 PortMappingPrintPrologue( context );
4718
4719 // Start operation.
4720
4721 sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4722 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
4723 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
4724 require_noerr( err, exit );
4725
4726 context->opRef = sdRef;
4727 if( !useMainConnection )
4728 {
4729 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4730 require_noerr( err, exit );
4731 }
4732
4733 dispatch_main();
4734
4735 exit:
4736 dispatch_source_forget( &signalSource );
4737 if( context ) PortMappingContextFree( context );
4738 if( err ) exit( 1 );
4739 }
4740
4741 //===========================================================================================================================
4742 // PortMappingPrintPrologue
4743 //===========================================================================================================================
4744
4745 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
4746 {
4747 char ifName[ kInterfaceNameBufLen ];
4748
4749 InterfaceIndexToName( inContext->ifIndex, ifName );
4750
4751 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
4752 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
4753 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
4754 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
4755 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
4756 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl,
4757 " (system will use a default value.)" );
4758 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4759 FPrintF( stdout, "---\n" );
4760
4761 }
4762
4763 //===========================================================================================================================
4764 // PortMappingContextFree
4765 //===========================================================================================================================
4766
4767 static void PortMappingContextFree( PortMappingContext *inContext )
4768 {
4769 DNSServiceForget( &inContext->opRef );
4770 DNSServiceForget( &inContext->mainRef );
4771 free( inContext );
4772 }
4773
4774 //===========================================================================================================================
4775 // PortMappingCallback
4776 //===========================================================================================================================
4777
4778 static void DNSSD_API
4779 PortMappingCallback(
4780 DNSServiceRef inSDRef,
4781 DNSServiceFlags inFlags,
4782 uint32_t inInterfaceIndex,
4783 DNSServiceErrorType inError,
4784 uint32_t inExternalIPv4Address,
4785 DNSServiceProtocol inProtocol,
4786 uint16_t inInternalPort,
4787 uint16_t inExternalPort,
4788 uint32_t inTTL,
4789 void * inContext )
4790 {
4791 PortMappingContext * const context = (PortMappingContext *) inContext;
4792 struct timeval now;
4793 char errorStr[ 128 ];
4794
4795 Unused( inSDRef );
4796 Unused( inFlags );
4797
4798 gettimeofday( &now, NULL );
4799
4800 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
4801 if( !context->printedHeader )
4802 {
4803 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4804 context->printedHeader = true;
4805 }
4806 FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4807 &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
4808 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
4809 }
4810
4811 //===========================================================================================================================
4812 // BrowseAllCmd
4813 //===========================================================================================================================
4814
4815 typedef struct BrowseAllConnection BrowseAllConnection;
4816
4817 typedef struct
4818 {
4819 ServiceBrowserRef browser; // Service browser.
4820 ServiceBrowserResults * results; // Results from the service browser.
4821 BrowseAllConnection * connectionList; // List of connections.
4822 dispatch_source_t connectionTimer; // Timer for connection timeout.
4823 int connectionPendingCount; // Number of pending connections.
4824 int connectionTimeoutSecs; // Timeout value for connections in seconds.
4825
4826 } BrowseAllContext;
4827
4828 struct BrowseAllConnection
4829 {
4830 BrowseAllConnection * next; // Next connection object in list.
4831 sockaddr_ip sip; // IPv4 or IPv6 address to connect to.
4832 uint16_t port; // TCP port to connect to.
4833 AsyncConnectionRef asyncCnx; // AsyncConnection object to handle the actual connection.
4834 OSStatus status; // Status of connection. NoErr means connection succeeded.
4835 CFTimeInterval connectTimeSecs; // Time it took to connect in seconds.
4836 int32_t refCount; // This object's reference count.
4837 BrowseAllContext * context; // Back pointer to parent context.
4838 };
4839
4840 static void _BrowseAllContextFree( BrowseAllContext *inContext );
4841 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
4842 static OSStatus
4843 _BrowseAllConnectionCreate(
4844 const struct sockaddr * inSockAddr,
4845 uint16_t inPort,
4846 BrowseAllContext * inContext,
4847 BrowseAllConnection ** outConnection );
4848 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
4849 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
4850 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
4851 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
4852 static void _BrowseAllExit( void *inContext );
4853
4854 static Boolean _IsServiceTypeTCP( const char *inServiceType );
4855
4856 static void BrowseAllCmd( void )
4857 {
4858 OSStatus err;
4859 BrowseAllContext * context = NULL;
4860 size_t i;
4861 uint32_t ifIndex;
4862 char ifName[ kInterfaceNameBufLen ];
4863
4864 // Check parameters.
4865
4866 if( gBrowseAll_BrowseTimeSecs <= 0 )
4867 {
4868 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
4869 err = kParamErr;
4870 goto exit;
4871 }
4872
4873 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
4874 require_action( context, exit, err = kNoMemoryErr );
4875
4876 context->connectionTimeoutSecs = gBrowseAll_ConnectTimeout;
4877 #if( TARGET_OS_POSIX )
4878 // Increase the open file descriptor limit for connection sockets.
4879
4880 if( context->connectionTimeoutSecs > 0 )
4881 {
4882 struct rlimit fdLimits;
4883
4884 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
4885 err = map_global_noerr_errno( err );
4886 require_noerr( err, exit );
4887
4888 if( fdLimits.rlim_cur < 4096 )
4889 {
4890 fdLimits.rlim_cur = 4096;
4891 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
4892 err = map_global_noerr_errno( err );
4893 require_noerr( err, exit );
4894 }
4895 }
4896 #endif
4897
4898 // Get interface index.
4899
4900 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4901 require_noerr_quiet( err, exit );
4902
4903 // Print prologue.
4904
4905 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4906 FPrintF( stdout, "Service types: ");
4907 if( gBrowseAll_ServiceTypesCount > 0 )
4908 {
4909 FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
4910 for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
4911 {
4912 FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
4913 }
4914 FPrintF( stdout, "\n" );
4915 }
4916 else
4917 {
4918 FPrintF( stdout, "all services\n" );
4919 }
4920 FPrintF( stdout, "Domain: %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
4921 FPrintF( stdout, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
4922 FPrintF( stdout, "Connect timeout: %d second%?c\n",
4923 context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
4924 FPrintF( stdout, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
4925 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
4926 FPrintF( stdout, "---\n" );
4927
4928 err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
4929 (unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
4930 require_noerr( err, exit );
4931
4932 for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
4933 {
4934 err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
4935 require_noerr( err, exit );
4936 }
4937 ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
4938 ServiceBrowserStart( context->browser );
4939 dispatch_main();
4940
4941 exit:
4942 if( context ) _BrowseAllContextFree( context );
4943 if( err ) exit( 1 );
4944 }
4945
4946 //===========================================================================================================================
4947 // _BrowseAllContextFree
4948 //===========================================================================================================================
4949
4950 static void _BrowseAllContextFree( BrowseAllContext *inContext )
4951 {
4952 check( !inContext->browser );
4953 check( !inContext->connectionTimer );
4954 check( !inContext->connectionList );
4955 ForgetServiceBrowserResults( &inContext->results );
4956 free( inContext );
4957 }
4958
4959 //===========================================================================================================================
4960 // _BrowseAllServiceBrowserCallback
4961 //===========================================================================================================================
4962
4963 #define kDiscardProtocolPort 9
4964
4965 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
4966 {
4967 OSStatus err;
4968 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4969 SBRDomain * domain;
4970 SBRServiceType * type;
4971 SBRServiceInstance * instance;
4972 SBRIPAddress * ipaddr;
4973
4974 Unused( inError );
4975
4976 require_action( inResults, exit, err = kUnexpectedErr );
4977
4978 check( !context->results );
4979 context->results = inResults;
4980 ServiceBrowserResultsRetain( context->results );
4981
4982 check( context->connectionPendingCount == 0 );
4983 if( context->connectionTimeoutSecs > 0 )
4984 {
4985 BrowseAllConnection * connection;
4986 BrowseAllConnection ** connectionPtr = &context->connectionList;
4987 char destination[ kSockAddrStringMaxSize ];
4988
4989 for( domain = context->results->domainList; domain; domain = domain->next )
4990 {
4991 for( type = domain->typeList; type; type = type->next )
4992 {
4993 if( !_IsServiceTypeTCP( type->name ) ) continue;
4994 for( instance = type->instanceList; instance; instance = instance->next )
4995 {
4996 if( instance->port == kDiscardProtocolPort ) continue;
4997 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
4998 {
4999 err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
5000 require_noerr( err, exit );
5001
5002 *connectionPtr = connection;
5003 connectionPtr = &connection->next;
5004
5005 err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
5006 check_noerr( err );
5007 if( !err )
5008 {
5009 err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
5010 kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
5011 kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5012 _BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
5013 dispatch_get_main_queue() );
5014 check_noerr( err );
5015 }
5016 if( !err )
5017 {
5018 _BrowseAllConnectionRetain( connection );
5019 connection->status = kInProgressErr;
5020 ++context->connectionPendingCount;
5021 }
5022 else
5023 {
5024 connection->status = err;
5025 }
5026 }
5027 }
5028 }
5029 }
5030 }
5031
5032 if( context->connectionPendingCount > 0 )
5033 {
5034 check( !context->connectionTimer );
5035 err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5036 100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5037 require_noerr( err, exit );
5038 dispatch_resume( context->connectionTimer );
5039 }
5040 else
5041 {
5042 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5043 }
5044 err = kNoErr;
5045
5046 exit:
5047 ForgetCF( &context->browser );
5048 if( err ) exit( 1 );
5049 }
5050
5051 //===========================================================================================================================
5052 // _BrowseAllConnectionCreate
5053 //===========================================================================================================================
5054
5055 static OSStatus
5056 _BrowseAllConnectionCreate(
5057 const struct sockaddr * inSockAddr,
5058 uint16_t inPort,
5059 BrowseAllContext * inContext,
5060 BrowseAllConnection ** outConnection )
5061 {
5062 OSStatus err;
5063 BrowseAllConnection * obj;
5064
5065 obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5066 require_action( obj, exit, err = kNoMemoryErr );
5067
5068 obj->refCount = 1;
5069 SockAddrCopy( inSockAddr, &obj->sip );
5070 obj->port = inPort;
5071 obj->context = inContext;
5072
5073 *outConnection = obj;
5074 err = kNoErr;
5075
5076 exit:
5077 return( err );
5078 }
5079
5080 //===========================================================================================================================
5081 // _BrowseAllConnectionRetain
5082 //===========================================================================================================================
5083
5084 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5085 {
5086 ++inConnection->refCount;
5087 }
5088
5089 //===========================================================================================================================
5090 // _BrowseAllConnectionRelease
5091 //===========================================================================================================================
5092
5093 static void _BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5094 {
5095 if( --inConnection->refCount == 0 ) free( inConnection );
5096 }
5097
5098 //===========================================================================================================================
5099 // _BrowseAllConnectionProgress
5100 //===========================================================================================================================
5101
5102 static void _BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5103 {
5104 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5105
5106 if( inPhase == kAsyncConnectionPhase_Connected )
5107 {
5108 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
5109
5110 connection->connectTimeSecs = info->connectSecs;
5111 }
5112 }
5113
5114 //===========================================================================================================================
5115 // _BrowseAllConnectionHandler
5116 //===========================================================================================================================
5117
5118 static void _BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5119 {
5120 BrowseAllConnection * const connection = (BrowseAllConnection *) inArg;
5121 BrowseAllContext * const context = connection->context;
5122
5123 connection->status = inError;
5124 ForgetSocket( &inSock );
5125 if( context )
5126 {
5127 check( context->connectionPendingCount > 0 );
5128 if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5129 {
5130 dispatch_source_forget( &context->connectionTimer );
5131 dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5132 }
5133 }
5134 _BrowseAllConnectionRelease( connection );
5135 }
5136
5137 //===========================================================================================================================
5138 // _BrowseAllExit
5139 //===========================================================================================================================
5140
5141 #define Indent( X ) ( (X) * 4 ), ""
5142
5143 static void _BrowseAllExit( void *inContext )
5144 {
5145 BrowseAllContext * const context = (BrowseAllContext *) inContext;
5146 SBRDomain * domain;
5147 SBRServiceType * type;
5148 SBRServiceInstance * instance;
5149 SBRIPAddress * ipaddr;
5150 char textBuf[ 512 ];
5151 #if( TARGET_OS_POSIX )
5152 const Boolean useColor = isatty( STDOUT_FILENO ) ? true : false;
5153 #endif
5154
5155 dispatch_source_forget( &context->connectionTimer );
5156
5157 for( domain = context->results->domainList; domain; domain = domain->next )
5158 {
5159 FPrintF( stdout, "%s\n\n", domain->name );
5160
5161 for( type = domain->typeList; type; type = type->next )
5162 {
5163 const char * description;
5164 const Boolean serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5165
5166 description = ServiceTypeDescription( type->name );
5167 if( description ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), description, type->name );
5168 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
5169
5170 for( instance = type->instanceList; instance; instance = instance->next )
5171 {
5172 char * dst = textBuf;
5173 char * const lim = &textBuf[ countof( textBuf ) ];
5174 char ifname[ IF_NAMESIZE + 1 ];
5175
5176 SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5177 if( instance->ifIndex == 0 )
5178 {
5179 SNPrintF_Add( &dst, lim, "the Internet" );
5180 }
5181 else if( if_indextoname( instance->ifIndex, ifname ) )
5182 {
5183 NetTransportType netType;
5184
5185 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5186 SNPrintF_Add( &dst, lim, "%s (%s)",
5187 ( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5188 ifname );
5189 }
5190 else
5191 {
5192 SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5193 }
5194 FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5195 Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5196
5197 if( instance->hostname )
5198 {
5199 SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5200 FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5201 Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5202 }
5203 else
5204 {
5205 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5206 }
5207
5208 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5209 {
5210 BrowseAllConnection * conn;
5211 BrowseAllConnection ** connPtr;
5212
5213 FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5214 Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5215
5216 conn = NULL;
5217 if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5218 {
5219 for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5220 {
5221 if( ( conn->port == instance->port ) &&
5222 ( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5223 }
5224 if( conn )
5225 {
5226 if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5227 *connPtr = conn->next;
5228 conn->context = NULL;
5229 AsyncConnection_Forget( &conn->asyncCnx );
5230 }
5231 }
5232
5233 if( conn )
5234 {
5235 if( conn->status == kNoErr )
5236 {
5237 FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5238 useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5239 }
5240 else
5241 {
5242 FPrintF( stdout, " (%scould not connect%s: %m)\n",
5243 useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5244 }
5245 _BrowseAllConnectionRelease( conn );
5246 }
5247 else
5248 {
5249 FPrintF( stdout, " (no connection attempted)\n" );
5250 }
5251 }
5252
5253 FPrintF( stdout, "\n" );
5254 if( instance->txtLen == 0 ) continue;
5255
5256 FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5257 Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5258 if( instance->txtLen > 1 )
5259 {
5260 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5261 }
5262 else
5263 {
5264 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5265 }
5266 FPrintF( stdout, "\n" );
5267 }
5268 FPrintF( stdout, "\n" );
5269 }
5270 }
5271
5272 _BrowseAllContextFree( context );
5273 Exit( NULL );
5274 }
5275
5276 //===========================================================================================================================
5277 // _IsServiceTypeTCP
5278 //===========================================================================================================================
5279
5280 static Boolean _IsServiceTypeTCP( const char *inServiceType )
5281 {
5282 OSStatus err;
5283 const uint8_t * secondLabel;
5284 uint8_t name[ kDomainNameLengthMax ];
5285
5286 err = DomainNameFromString( name, inServiceType, NULL );
5287 if( !err )
5288 {
5289 secondLabel = NextLabel( name );
5290 if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5291 }
5292 return( false );
5293 }
5294
5295 //===========================================================================================================================
5296 // GetNameInfoCmd
5297 //===========================================================================================================================
5298
5299 const FlagStringPair kGetNameInfoFlagStringPairs[] =
5300 {
5301 CaseFlagStringify( NI_NUMERICSCOPE ),
5302 CaseFlagStringify( NI_DGRAM ),
5303 CaseFlagStringify( NI_NUMERICSERV ),
5304 CaseFlagStringify( NI_NAMEREQD ),
5305 CaseFlagStringify( NI_NUMERICHOST ),
5306 CaseFlagStringify( NI_NOFQDN ),
5307 { 0, NULL }
5308 };
5309
5310 static void GetNameInfoCmd( void )
5311 {
5312 OSStatus err;
5313 sockaddr_ip sip;
5314 size_t sockAddrLen;
5315 unsigned int flags;
5316 const FlagStringPair * pair;
5317 struct timeval now;
5318 char host[ NI_MAXHOST ];
5319 char serv[ NI_MAXSERV ];
5320
5321 err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5322 check_noerr( err );
5323 if( err )
5324 {
5325 FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5326 goto exit;
5327 }
5328
5329 flags = 0;
5330 if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM;
5331 if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD;
5332 if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN;
5333 if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST;
5334 if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE;
5335 if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV;
5336
5337 // Print prologue.
5338
5339 FPrintF( stdout, "SockAddr: %##a\n", &sip.sa );
5340 FPrintF( stdout, "Flags: 0x%X < ", flags );
5341 for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
5342 {
5343 if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
5344 }
5345 FPrintF( stdout, ">\n" );
5346 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5347 FPrintF( stdout, "---\n" );
5348
5349 // Call getnameinfo().
5350
5351 err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
5352 (int) flags );
5353 gettimeofday( &now, NULL );
5354 if( err )
5355 {
5356 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
5357 }
5358 else
5359 {
5360 FPrintF( stdout, "host: %s\n", host );
5361 FPrintF( stdout, "serv: %s\n", serv );
5362 }
5363 FPrintF( stdout, "---\n" );
5364 FPrintF( stdout, "End time: %{du:time}\n", &now );
5365
5366 exit:
5367 gExitCode = err ? 1 : 0;
5368 }
5369
5370 //===========================================================================================================================
5371 // GetAddrInfoStressCmd
5372 //===========================================================================================================================
5373
5374 typedef struct
5375 {
5376 DNSServiceRef mainRef;
5377 DNSServiceRef sdRef;
5378 DNSServiceFlags flags;
5379 unsigned int interfaceIndex;
5380 unsigned int connectionNumber;
5381 unsigned int requestCount;
5382 unsigned int requestCountMax;
5383 unsigned int requestCountLimit;
5384 unsigned int durationMinMs;
5385 unsigned int durationMaxMs;
5386
5387 } GAIStressContext;
5388
5389 static void GetAddrInfoStressEvent( void *inContext );
5390 static void DNSSD_API
5391 GetAddrInfoStressCallback(
5392 DNSServiceRef inSDRef,
5393 DNSServiceFlags inFlags,
5394 uint32_t inInterfaceIndex,
5395 DNSServiceErrorType inError,
5396 const char * inHostname,
5397 const struct sockaddr * inSockAddr,
5398 uint32_t inTTL,
5399 void * inContext );
5400
5401 static void GetAddrInfoStressCmd( void )
5402 {
5403 OSStatus err;
5404 GAIStressContext * context = NULL;
5405 int i;
5406 DNSServiceFlags flags;
5407 uint32_t ifIndex;
5408 char ifName[ kInterfaceNameBufLen ];
5409
5410 if( gGAIStress_TestDurationSecs < 0 )
5411 {
5412 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
5413 err = kParamErr;
5414 goto exit;
5415 }
5416 if( gGAIStress_ConnectionCount <= 0 )
5417 {
5418 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
5419 err = kParamErr;
5420 goto exit;
5421 }
5422 if( gGAIStress_DurationMinMs <= 0 )
5423 {
5424 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
5425 err = kParamErr;
5426 goto exit;
5427 }
5428 if( gGAIStress_DurationMaxMs <= 0 )
5429 {
5430 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
5431 err = kParamErr;
5432 goto exit;
5433 }
5434 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
5435 {
5436 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5437 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
5438 err = kParamErr;
5439 goto exit;
5440 }
5441 if( gGAIStress_RequestCountMax <= 0 )
5442 {
5443 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
5444 err = kParamErr;
5445 goto exit;
5446 }
5447
5448 // Set flags.
5449
5450 flags = GetDNSSDFlagsFromOpts();
5451
5452 // Set interface index.
5453
5454 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5455 require_noerr_quiet( err, exit );
5456
5457 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
5458 {
5459 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
5460 require_action( context, exit, err = kNoMemoryErr );
5461
5462 context->flags = flags;
5463 context->interfaceIndex = ifIndex;
5464 context->connectionNumber = (unsigned int)( i + 1 );
5465 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
5466 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
5467 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
5468
5469 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5470 context = NULL;
5471 }
5472
5473 if( gGAIStress_TestDurationSecs > 0 )
5474 {
5475 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
5476 }
5477
5478 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
5479 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5480 FPrintF( stdout, "Test duration: " );
5481 if( gGAIStress_TestDurationSecs == 0 )
5482 {
5483 FPrintF( stdout, "∞\n" );
5484 }
5485 else
5486 {
5487 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
5488 }
5489 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
5490 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
5491 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
5492 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
5493 FPrintF( stdout, "Start time: %{du:time}\n", NULL);
5494 FPrintF( stdout, "---\n" );
5495
5496 dispatch_main();
5497
5498 exit:
5499 FreeNullSafe( context );
5500 if( err ) exit( 1 );
5501 }
5502
5503 //===========================================================================================================================
5504 // GetAddrInfoStressEvent
5505 //===========================================================================================================================
5506
5507 #define kStressRandStrLen 5
5508
5509 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5510
5511 static void GetAddrInfoStressEvent( void *inContext )
5512 {
5513 GAIStressContext * const context = (GAIStressContext *) inContext;
5514 OSStatus err;
5515 DNSServiceRef sdRef;
5516 unsigned int nextMs;
5517 char randomStr[ kStressRandStrLen + 1 ];
5518 char hostname[ kStressRandStrLen + 4 + 1 ];
5519 Boolean isConnectionNew = false;
5520 static Boolean printedHeader = false;
5521
5522 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
5523 {
5524 DNSServiceForget( &context->mainRef );
5525 context->sdRef = NULL;
5526 context->requestCount = 0;
5527 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
5528
5529 err = DNSServiceCreateConnection( &context->mainRef );
5530 require_noerr( err, exit );
5531
5532 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
5533 require_noerr( err, exit );
5534
5535 isConnectionNew = true;
5536 }
5537
5538 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
5539 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
5540
5541 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
5542
5543 if( !printedHeader )
5544 {
5545 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5546 printedHeader = true;
5547 }
5548 FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
5549 NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
5550
5551 DNSServiceForget( &context->sdRef );
5552 sdRef = context->mainRef;
5553 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
5554 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
5555 require_noerr( err, exit );
5556 context->sdRef = sdRef;
5557
5558 context->requestCount++;
5559
5560 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
5561
5562 exit:
5563 if( err ) exit( 1 );
5564 }
5565
5566 //===========================================================================================================================
5567 // GetAddrInfoStressCallback
5568 //===========================================================================================================================
5569
5570 static void DNSSD_API
5571 GetAddrInfoStressCallback(
5572 DNSServiceRef inSDRef,
5573 DNSServiceFlags inFlags,
5574 uint32_t inInterfaceIndex,
5575 DNSServiceErrorType inError,
5576 const char * inHostname,
5577 const struct sockaddr * inSockAddr,
5578 uint32_t inTTL,
5579 void * inContext )
5580 {
5581 Unused( inSDRef );
5582 Unused( inFlags );
5583 Unused( inInterfaceIndex );
5584 Unused( inError );
5585 Unused( inHostname );
5586 Unused( inSockAddr );
5587 Unused( inTTL );
5588 Unused( inContext );
5589 }
5590
5591 //===========================================================================================================================
5592 // DNSQueryCmd
5593 //===========================================================================================================================
5594
5595 typedef struct
5596 {
5597 sockaddr_ip serverAddr;
5598 uint64_t sendTicks;
5599 uint8_t * msgPtr;
5600 size_t msgLen;
5601 size_t msgOffset;
5602 const char * name;
5603 dispatch_source_t readSource;
5604 SocketRef sock;
5605 int timeLimitSecs;
5606 uint16_t queryID;
5607 uint16_t type;
5608 Boolean haveTCPLen;
5609 Boolean useTCP;
5610 Boolean printRawRData; // True if RDATA results are not to be formatted.
5611 uint8_t msgBuf[ 512 ];
5612
5613 } DNSQueryContext;
5614
5615 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
5616 static void DNSQueryReadHandler( void *inContext );
5617 static void DNSQueryCancelHandler( void *inContext );
5618
5619 static void DNSQueryCmd( void )
5620 {
5621 OSStatus err;
5622 DNSQueryContext * context = NULL;
5623 uint8_t * msgPtr;
5624 size_t msgLen, sendLen;
5625
5626 // Check command parameters.
5627
5628 if( gDNSQuery_TimeLimitSecs < -1 )
5629 {
5630 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5631 err = kParamErr;
5632 goto exit;
5633 }
5634 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5635 {
5636 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5637 err = kParamErr;
5638 goto exit;
5639 }
5640
5641 // Create context.
5642
5643 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5644 require_action( context, exit, err = kNoMemoryErr );
5645
5646 context->name = gDNSQuery_Name;
5647 context->sock = kInvalidSocketRef;
5648 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5649 context->queryID = (uint16_t) Random32();
5650 context->useTCP = gDNSQuery_UseTCP ? true : false;
5651 context->printRawRData = gDNSQuery_RawRData ? true : false;
5652
5653 #if( TARGET_OS_DARWIN )
5654 if( gDNSQuery_Server )
5655 #endif
5656 {
5657 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5658 require_noerr( err, exit );
5659 }
5660 #if( TARGET_OS_DARWIN )
5661 else
5662 {
5663 err = GetDefaultDNSServer( &context->serverAddr );
5664 require_noerr( err, exit );
5665 }
5666 #endif
5667 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5668
5669 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5670 require_noerr( err, exit );
5671
5672 // Write query message.
5673
5674 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5675
5676 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5677 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5678 kDNSServiceClass_IN, &msgLen );
5679 require_noerr( err, exit );
5680 check( msgLen <= UINT16_MAX );
5681
5682 if( context->useTCP )
5683 {
5684 WriteBig16( context->msgBuf, msgLen );
5685 sendLen = 2 + msgLen;
5686 }
5687 else
5688 {
5689 sendLen = msgLen;
5690 }
5691
5692 DNSQueryPrintPrologue( context );
5693
5694 if( gDNSQuery_Verbose )
5695 {
5696 FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
5697 FPrintF( stdout, "---\n" );
5698 }
5699
5700 if( context->useTCP )
5701 {
5702 // Create TCP socket.
5703
5704 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5705 err = map_socket_creation_errno( context->sock );
5706 require_noerr( err, exit );
5707
5708 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5709 require_noerr( err, exit );
5710 }
5711 else
5712 {
5713 // Create UDP socket.
5714
5715 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5716 require_noerr( err, exit );
5717 }
5718
5719 context->sendTicks = UpTicks();
5720 err = SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5721 require_noerr( err, exit );
5722
5723 if( context->timeLimitSecs == 0 ) goto exit;
5724
5725 err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5726 &context->readSource );
5727 require_noerr( err, exit );
5728 dispatch_resume( context->readSource );
5729
5730 if( context->timeLimitSecs > 0 )
5731 {
5732 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5733 Exit );
5734 }
5735 dispatch_main();
5736
5737 exit:
5738 if( context )
5739 {
5740 dispatch_source_forget( &context->readSource );
5741 ForgetSocket( &context->sock );
5742 free( context );
5743 }
5744 if( err ) exit( 1 );
5745 }
5746
5747 //===========================================================================================================================
5748 // DNSQueryPrintPrologue
5749 //===========================================================================================================================
5750
5751 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5752 {
5753 const int timeLimitSecs = inContext->timeLimitSecs;
5754
5755 FPrintF( stdout, "Name: %s\n", inContext->name );
5756 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5757 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5758 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5759 FPrintF( stdout, "Time limit: " );
5760 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5761 else FPrintF( stdout, "∞\n" );
5762 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
5763 FPrintF( stdout, "---\n" );
5764 }
5765
5766 //===========================================================================================================================
5767 // DNSQueryReadHandler
5768 //===========================================================================================================================
5769
5770 static void DNSQueryReadHandler( void *inContext )
5771 {
5772 OSStatus err;
5773 struct timeval now;
5774 const uint64_t nowTicks = UpTicks();
5775 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5776
5777 gettimeofday( &now, NULL );
5778
5779 if( context->useTCP )
5780 {
5781 if( !context->haveTCPLen )
5782 {
5783 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5784 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5785 require_noerr( err, exit );
5786
5787 context->msgOffset = 0;
5788 context->msgLen = ReadBig16( context->msgBuf );
5789 context->haveTCPLen = true;
5790 if( context->msgLen <= sizeof( context->msgBuf ) )
5791 {
5792 context->msgPtr = context->msgBuf;
5793 }
5794 else
5795 {
5796 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5797 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5798 }
5799 }
5800
5801 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5802 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5803 require_noerr( err, exit );
5804 context->msgOffset = 0;
5805 context->haveTCPLen = false;
5806 }
5807 else
5808 {
5809 sockaddr_ip fromAddr;
5810
5811 context->msgPtr = context->msgBuf;
5812 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5813 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5814 require_noerr( err, exit );
5815
5816 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5817 }
5818
5819 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
5820 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5821 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5822 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5823 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
5824
5825 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5826 {
5827 Exit( kExitReason_ReceivedResponse );
5828 }
5829
5830 exit:
5831 if( err ) dispatch_source_forget( &context->readSource );
5832 }
5833
5834 //===========================================================================================================================
5835 // DNSQueryCancelHandler
5836 //===========================================================================================================================
5837
5838 static void DNSQueryCancelHandler( void *inContext )
5839 {
5840 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5841
5842 check( !context->readSource );
5843 ForgetSocket( &context->sock );
5844 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5845 free( context );
5846 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5847 }
5848
5849 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5850 //===========================================================================================================================
5851 // DNSCryptCmd
5852 //===========================================================================================================================
5853
5854 #define kDNSCryptPort 443
5855
5856 #define kDNSCryptMinPadLength 8
5857 #define kDNSCryptMaxPadLength 256
5858 #define kDNSCryptBlockSize 64
5859 #define kDNSCryptCertMinimumLength 124
5860 #define kDNSCryptClientMagicLength 8
5861 #define kDNSCryptResolverMagicLength 8
5862 #define kDNSCryptHalfNonceLength 12
5863 #define kDNSCryptCertMagicLength 4
5864
5865 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5866
5867 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5868 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5869 {
5870 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5871 };
5872
5873 typedef struct
5874 {
5875 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5876 uint8_t esVersion[ 2 ];
5877 uint8_t minorVersion[ 2 ];
5878 uint8_t signature[ crypto_sign_BYTES ];
5879 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5880 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5881 uint8_t serial[ 4 ];
5882 uint8_t startTime[ 4 ];
5883 uint8_t endTime[ 4 ];
5884 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5885
5886 } DNSCryptCert;
5887
5888 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5889
5890 typedef struct
5891 {
5892 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5893 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5894 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5895 uint8_t poly1305MAC[ 16 ];
5896
5897 } DNSCryptQueryHeader;
5898
5899 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5900 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5901 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5902 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5903
5904 typedef struct
5905 {
5906 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5907 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5908 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5909 uint8_t poly1305MAC[ 16 ];
5910
5911 } DNSCryptResponseHeader;
5912
5913 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5914 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5915 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5916 sizeof( DNSCryptResponseHeader ) );
5917
5918 typedef struct
5919 {
5920 sockaddr_ip serverAddr;
5921 uint64_t sendTicks;
5922 const char * providerName;
5923 const char * qname;
5924 const uint8_t * certPtr;
5925 size_t certLen;
5926 dispatch_source_t readSource;
5927 size_t msgLen;
5928 int timeLimitSecs;
5929 uint16_t queryID;
5930 uint16_t qtype;
5931 Boolean printRawRData;
5932 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5933 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5934 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5935 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5936 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5937 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5938 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5939 uint8_t msgBuf[ 512 ];
5940
5941 } DNSCryptContext;
5942
5943 static void DNSCryptReceiveCertHandler( void *inContext );
5944 static void DNSCryptReceiveResponseHandler( void *inContext );
5945 static void DNSCryptProceed( void *inContext );
5946 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5947 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5948 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5949 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5950
5951 static void DNSCryptCmd( void )
5952 {
5953 OSStatus err;
5954 DNSCryptContext * context = NULL;
5955 size_t writtenBytes;
5956 size_t totalBytes;
5957 SocketContext * sockCtx;
5958 SocketRef sock = kInvalidSocketRef;
5959 const char * ptr;
5960
5961 // Check command parameters.
5962
5963 if( gDNSCrypt_TimeLimitSecs < -1 )
5964 {
5965 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5966 err = kParamErr;
5967 goto exit;
5968 }
5969
5970 // Create context.
5971
5972 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5973 require_action( context, exit, err = kNoMemoryErr );
5974
5975 context->providerName = gDNSCrypt_ProviderName;
5976 context->qname = gDNSCrypt_Name;
5977 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5978 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5979
5980 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5981 require_noerr( err, exit );
5982
5983 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5984 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5985 if( err || ( *ptr != '\0' ) )
5986 {
5987 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5988 goto exit;
5989 }
5990 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5991 {
5992 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5993 totalBytes, sizeof( context->serverPublicSignKey ) );
5994 err = kSizeErr;
5995 goto exit;
5996 }
5997 check( writtenBytes == totalBytes );
5998
5999 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6000 require_noerr( err, exit );
6001 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
6002
6003 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
6004 require_noerr( err, exit );
6005
6006 // Write query message.
6007
6008 context->queryID = (uint16_t) Random32();
6009 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6010 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6011 require_noerr( err, exit );
6012
6013 // Create UDP socket.
6014
6015 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6016 require_noerr( err, exit );
6017
6018 // Send DNS query.
6019
6020 context->sendTicks = UpTicks();
6021 err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6022 require_noerr( err, exit );
6023
6024 err = SocketContextCreate( sock, context, &sockCtx );
6025 require_noerr( err, exit );
6026 sock = kInvalidSocketRef;
6027
6028 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6029 &context->readSource );
6030 if( err ) ForgetSocketContext( &sockCtx );
6031 require_noerr( err, exit );
6032
6033 dispatch_resume( context->readSource );
6034
6035 if( context->timeLimitSecs > 0 )
6036 {
6037 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6038 Exit );
6039 }
6040 dispatch_main();
6041
6042 exit:
6043 if( context ) free( context );
6044 ForgetSocket( &sock );
6045 if( err ) exit( 1 );
6046 }
6047
6048 //===========================================================================================================================
6049 // DNSCryptReceiveCertHandler
6050 //===========================================================================================================================
6051
6052 static void DNSCryptReceiveCertHandler( void *inContext )
6053 {
6054 OSStatus err;
6055 struct timeval now;
6056 const uint64_t nowTicks = UpTicks();
6057 SocketContext * const sockCtx = (SocketContext *) inContext;
6058 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6059 const DNSHeader * hdr;
6060 sockaddr_ip fromAddr;
6061 const uint8_t * ptr;
6062 const uint8_t * txtPtr;
6063 size_t txtLen;
6064 unsigned int answerCount, i;
6065 uint8_t targetName[ kDomainNameLengthMax ];
6066
6067 gettimeofday( &now, NULL );
6068
6069 dispatch_source_forget( &context->readSource );
6070
6071 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6072 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6073 require_noerr( err, exit );
6074 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6075
6076 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6077 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6078 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6079 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6080 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
6081
6082 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6083
6084 hdr = (DNSHeader *) context->msgBuf;
6085 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6086
6087 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6088 require_noerr( err, exit );
6089
6090 err = DomainNameFromString( targetName, context->providerName, NULL );
6091 require_noerr( err, exit );
6092
6093 answerCount = DNSHeaderGetAnswerCount( hdr );
6094 for( i = 0; i < answerCount; ++i )
6095 {
6096 uint16_t type;
6097 uint16_t class;
6098 uint8_t name[ kDomainNameLengthMax ];
6099
6100 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6101 &ptr );
6102 require_noerr( err, exit );
6103
6104 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6105 {
6106 break;
6107 }
6108 }
6109
6110 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6111 {
6112 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6113 err = kSizeErr;
6114 goto exit;
6115 }
6116 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6117 {
6118 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6119 err = kSizeErr;
6120 goto exit;
6121 }
6122
6123 context->certLen = txtPtr[ 0 ];
6124 context->certPtr = &txtPtr[ 1 ];
6125
6126 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6127
6128 exit:
6129 if( err ) Exit( NULL );
6130 }
6131
6132 //===========================================================================================================================
6133 // DNSCryptReceiveResponseHandler
6134 //===========================================================================================================================
6135
6136 static void DNSCryptReceiveResponseHandler( void *inContext )
6137 {
6138 OSStatus err;
6139 struct timeval now;
6140 const uint64_t nowTicks = UpTicks();
6141 SocketContext * const sockCtx = (SocketContext *) inContext;
6142 DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext;
6143 sockaddr_ip fromAddr;
6144 DNSCryptResponseHeader * hdr;
6145 const uint8_t * end;
6146 uint8_t * ciphertext;
6147 uint8_t * plaintext;
6148 const uint8_t * response;
6149 uint8_t nonce[ crypto_box_NONCEBYTES ];
6150
6151 gettimeofday( &now, NULL );
6152
6153 dispatch_source_forget( &context->readSource );
6154
6155 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6156 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6157 require_noerr( err, exit );
6158 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6159
6160 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6161 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
6162 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
6163 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6164
6165 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6166 {
6167 FPrintF( stderr, "DNSCrypt response is too short.\n" );
6168 err = kSizeErr;
6169 goto exit;
6170 }
6171
6172 hdr = (DNSCryptResponseHeader *) context->msgBuf;
6173
6174 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6175 {
6176 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6177 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
6178 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6179 err = kValueErr;
6180 goto exit;
6181 }
6182
6183 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6184 {
6185 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6186 err = kValueErr;
6187 goto exit;
6188 }
6189
6190 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6191
6192 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6193 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6194
6195 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6196 check( plaintext == ciphertext );
6197
6198 end = context->msgBuf + context->msgLen;
6199
6200 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6201 require_noerr( err, exit );
6202
6203 response = plaintext + crypto_box_ZEROBYTES;
6204 FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
6205 Exit( kExitReason_ReceivedResponse );
6206
6207 exit:
6208 if( err ) Exit( NULL );
6209 }
6210
6211 //===========================================================================================================================
6212 // DNSCryptProceed
6213 //===========================================================================================================================
6214
6215 static void DNSCryptProceed( void *inContext )
6216 {
6217 OSStatus err;
6218 DNSCryptContext * const context = (DNSCryptContext *) inContext;
6219
6220 err = DNSCryptProcessCert( context );
6221 require_noerr_quiet( err, exit );
6222
6223 err = DNSCryptBuildQuery( context );
6224 require_noerr_quiet( err, exit );
6225
6226 err = DNSCryptSendQuery( context );
6227 require_noerr_quiet( err, exit );
6228
6229 exit:
6230 if( err ) Exit( NULL );
6231 }
6232
6233 //===========================================================================================================================
6234 // DNSCryptProcessCert
6235 //===========================================================================================================================
6236
6237 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
6238 {
6239 OSStatus err;
6240 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
6241 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
6242 struct timeval now;
6243 time_t startTimeSecs, endTimeSecs;
6244 size_t signedLen;
6245 uint8_t * tempBuf;
6246 unsigned long long tempLen;
6247
6248 DNSCryptPrintCertificate( cert, inContext->certLen );
6249
6250 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6251 {
6252 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6253 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
6254 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6255 err = kValueErr;
6256 goto exit;
6257 }
6258
6259 startTimeSecs = (time_t) ReadBig32( cert->startTime );
6260 endTimeSecs = (time_t) ReadBig32( cert->endTime );
6261
6262 gettimeofday( &now, NULL );
6263 if( now.tv_sec < startTimeSecs )
6264 {
6265 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6266 err = kDateErr;
6267 goto exit;
6268 }
6269 if( now.tv_sec >= endTimeSecs )
6270 {
6271 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6272 err = kDateErr;
6273 goto exit;
6274 }
6275
6276 signedLen = (size_t)( certEnd - cert->signature );
6277 tempBuf = (uint8_t *) malloc( signedLen );
6278 require_action( tempBuf, exit, err = kNoMemoryErr );
6279 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6280 free( tempBuf );
6281 if( err )
6282 {
6283 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6284 err = kAuthenticationErr;
6285 goto exit;
6286 }
6287
6288 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
6289 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
6290
6291 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6292 require_noerr( err, exit );
6293
6294 inContext->certPtr = NULL;
6295 inContext->certLen = 0;
6296 inContext->msgLen = 0;
6297
6298 exit:
6299 return( err );
6300 }
6301
6302 //===========================================================================================================================
6303 // DNSCryptBuildQuery
6304 //===========================================================================================================================
6305
6306 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6307
6308 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
6309 {
6310 OSStatus err;
6311 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
6312 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
6313 size_t queryLen;
6314 size_t paddedQueryLen;
6315 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
6316 const uint8_t * padLimit;
6317 uint8_t nonce[ crypto_box_NONCEBYTES ];
6318
6319 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
6320
6321 inContext->queryID = (uint16_t) Random32();
6322 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
6323 inContext->qtype, kDNSServiceClass_IN, &queryLen );
6324 require_noerr( err, exit );
6325
6326 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
6327 if( padLimit > msgLimit ) padLimit = msgLimit;
6328
6329 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
6330 require_noerr( err, exit );
6331
6332 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
6333 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
6334 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
6335 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
6336
6337 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
6338 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
6339 require_noerr( err, exit );
6340
6341 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
6342 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
6343 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
6344
6345 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
6346
6347 exit:
6348 return( err );
6349 }
6350
6351 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
6352 {
6353 OSStatus err;
6354 size_t paddedLen;
6355
6356 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
6357
6358 paddedLen = inMsgLen + kDNSCryptMinPadLength +
6359 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
6360 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
6361 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
6362
6363 inMsgPtr[ inMsgLen ] = 0x80;
6364 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
6365
6366 if( outPaddedLen ) *outPaddedLen = paddedLen;
6367 err = kNoErr;
6368
6369 exit:
6370 return( err );
6371 }
6372
6373 //===========================================================================================================================
6374 // DNSCryptSendQuery
6375 //===========================================================================================================================
6376
6377 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
6378 {
6379 OSStatus err;
6380 SocketContext * sockCtx;
6381 SocketRef sock = kInvalidSocketRef;
6382
6383 check( inContext->msgLen > 0 );
6384 check( !inContext->readSource );
6385
6386 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
6387 require_noerr( err, exit );
6388
6389 inContext->sendTicks = UpTicks();
6390 err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
6391 require_noerr( err, exit );
6392
6393 err = SocketContextCreate( sock, inContext, &sockCtx );
6394 require_noerr( err, exit );
6395 sock = kInvalidSocketRef;
6396
6397 err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
6398 &inContext->readSource );
6399 if( err ) ForgetSocketContext( &sockCtx );
6400 require_noerr( err, exit );
6401
6402 dispatch_resume( inContext->readSource );
6403
6404 exit:
6405 ForgetSocket( &sock );
6406 return( err );
6407 }
6408
6409 //===========================================================================================================================
6410 // DNSCryptPrintCertificate
6411 //===========================================================================================================================
6412
6413 #define kCertTimeStrBufLen 32
6414
6415 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
6416
6417 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
6418 {
6419 time_t startTime, endTime;
6420 int extLen;
6421 char timeBuf[ kCertTimeStrBufLen ];
6422
6423 check( inLen >= kDNSCryptCertMinimumLength );
6424
6425 startTime = (time_t) ReadBig32( inCert->startTime );
6426 endTime = (time_t) ReadBig32( inCert->endTime );
6427
6428 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
6429 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
6430 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
6431 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
6432 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
6433 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
6434 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
6435 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
6436 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
6437 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
6438 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
6439
6440 if( inLen > kDNSCryptCertMinimumLength )
6441 {
6442 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
6443 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
6444 }
6445 FPrintF( stdout, "\n" );
6446 }
6447
6448 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
6449 {
6450 struct tm * tm;
6451
6452 tm = localtime( &inTime );
6453 if( !tm )
6454 {
6455 dlogassert( "localtime() returned a NULL pointer.\n" );
6456 *inBuffer = '\0';
6457 }
6458 else
6459 {
6460 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
6461 }
6462
6463 return( inBuffer );
6464 }
6465
6466 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6467
6468 //===========================================================================================================================
6469 // MDNSQueryCmd
6470 //===========================================================================================================================
6471
6472 typedef struct
6473 {
6474 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
6475 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6476 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6477 int localPort; // The port number to which the sockets are bound.
6478 int receiveSecs; // After send, the amount of time to spend receiving.
6479 uint32_t ifIndex; // Index of the interface over which to send the query.
6480 uint16_t qtype; // The type (QTYPE) of the record being queried.
6481 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
6482 Boolean allResponses; // True if all mDNS messages received should be printed.
6483 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
6484 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6485 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6486 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
6487 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
6488 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
6489
6490 } MDNSQueryContext;
6491
6492 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
6493 static void MDNSQueryReadHandler( void *inContext );
6494
6495 static void MDNSQueryCmd( void )
6496 {
6497 OSStatus err;
6498 MDNSQueryContext * context;
6499 SocketRef sockV4 = kInvalidSocketRef;
6500 SocketRef sockV6 = kInvalidSocketRef;
6501 ssize_t n;
6502 const char * ifname;
6503 size_t msgLen;
6504 unsigned int sendCount;
6505
6506 // Check command parameters.
6507
6508 if( gMDNSQuery_ReceiveSecs < -1 )
6509 {
6510 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
6511 err = kParamErr;
6512 goto exit;
6513 }
6514
6515 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
6516 require_action( context, exit, err = kNoMemoryErr );
6517
6518 context->qnameStr = gMDNSQuery_Name;
6519 context->receiveSecs = gMDNSQuery_ReceiveSecs;
6520 context->isQU = gMDNSQuery_IsQU ? true : false;
6521 context->allResponses = gMDNSQuery_AllResponses ? true : false;
6522 context->printRawRData = gMDNSQuery_RawRData ? true : false;
6523 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
6524 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
6525
6526 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
6527 require_noerr_quiet( err, exit );
6528
6529 ifname = if_indextoname( context->ifIndex, context->ifName );
6530 require_action( ifname, exit, err = kNameErr );
6531
6532 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
6533 require_noerr( err, exit );
6534
6535 // Set up IPv4 socket.
6536
6537 if( context->useIPv4 )
6538 {
6539 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6540 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6541 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
6542 require_noerr( err, exit );
6543 }
6544
6545 // Set up IPv6 socket.
6546
6547 if( context->useIPv6 )
6548 {
6549 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6550 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
6551 ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
6552 require_noerr( err, exit );
6553 }
6554
6555 // Craft mDNS query message.
6556
6557 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
6558 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
6559 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
6560 require_noerr( err, exit );
6561
6562 // Print prologue.
6563
6564 MDNSQueryPrintPrologue( context );
6565
6566 // Send mDNS query message.
6567
6568 sendCount = 0;
6569 if( IsValidSocket( sockV4 ) )
6570 {
6571 const struct sockaddr * const mcastAddr4 = GetMDNSMulticastAddrV4();
6572
6573 n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
6574 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6575 if( err )
6576 {
6577 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6578 ForgetSocket( &sockV4 );
6579 }
6580 else
6581 {
6582 ++sendCount;
6583 }
6584 }
6585 if( IsValidSocket( sockV6 ) )
6586 {
6587 const struct sockaddr * const mcastAddr6 = GetMDNSMulticastAddrV6();
6588
6589 n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
6590 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6591 if( err )
6592 {
6593 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6594 ForgetSocket( &sockV6 );
6595 }
6596 else
6597 {
6598 ++sendCount;
6599 }
6600 }
6601 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6602
6603 // If there's no wait period after the send, then exit.
6604
6605 if( context->receiveSecs == 0 ) goto exit;
6606
6607 // Create dispatch read sources for socket(s).
6608
6609 if( IsValidSocket( sockV4 ) )
6610 {
6611 SocketContext * sockCtx;
6612
6613 err = SocketContextCreate( sockV4, context, &sockCtx );
6614 require_noerr( err, exit );
6615 sockV4 = kInvalidSocketRef;
6616
6617 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6618 &context->readSourceV4 );
6619 if( err ) ForgetSocketContext( &sockCtx );
6620 require_noerr( err, exit );
6621
6622 dispatch_resume( context->readSourceV4 );
6623 }
6624
6625 if( IsValidSocket( sockV6 ) )
6626 {
6627 SocketContext * sockCtx;
6628
6629 err = SocketContextCreate( sockV6, context, &sockCtx );
6630 require_noerr( err, exit );
6631 sockV6 = kInvalidSocketRef;
6632
6633 err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
6634 &context->readSourceV6 );
6635 if( err ) ForgetSocketContext( &sockCtx );
6636 require_noerr( err, exit );
6637
6638 dispatch_resume( context->readSourceV6 );
6639 }
6640
6641 if( context->receiveSecs > 0 )
6642 {
6643 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6644 Exit );
6645 }
6646 dispatch_main();
6647
6648 exit:
6649 ForgetSocket( &sockV4 );
6650 ForgetSocket( &sockV6 );
6651 if( err ) exit( 1 );
6652 }
6653
6654 //===========================================================================================================================
6655 // MDNSColliderCmd
6656 //===========================================================================================================================
6657
6658 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
6659
6660 static void MDNSColliderCmd( void )
6661 {
6662 OSStatus err;
6663 MDNSColliderRef collider = NULL;
6664 uint8_t * rdataPtr = NULL;
6665 size_t rdataLen = 0;
6666 const char * ifname;
6667 uint32_t ifIndex;
6668 MDNSColliderProtocols protocols;
6669 uint16_t type;
6670 char ifName[ IF_NAMESIZE + 1 ];
6671 uint8_t name[ kDomainNameLengthMax ];
6672
6673 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6674 require_noerr_quiet( err, exit );
6675
6676 ifname = if_indextoname( ifIndex, ifName );
6677 if( !ifname )
6678 {
6679 FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
6680 err = kNameErr;
6681 goto exit;
6682 }
6683
6684 err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
6685 if( err )
6686 {
6687 FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
6688 goto exit;
6689 }
6690
6691 err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
6692 require_noerr_quiet( err, exit );
6693
6694 if( gMDNSCollider_RecordData )
6695 {
6696 err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
6697 require_noerr_quiet( err, exit );
6698 }
6699
6700 err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
6701 require_noerr( err, exit );
6702
6703 err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
6704 if( err )
6705 {
6706 FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
6707 goto exit;
6708 }
6709
6710 err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
6711 require_noerr( err, exit );
6712 ForgetMem( &rdataPtr );
6713
6714 protocols = kMDNSColliderProtocol_None;
6715 if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
6716 if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
6717 MDNSColliderSetProtocols( collider, protocols );
6718 MDNSColliderSetInterfaceIndex( collider, ifIndex );
6719 MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
6720
6721 err = MDNSColliderStart( collider );
6722 require_noerr( err, exit );
6723
6724 dispatch_main();
6725
6726 exit:
6727 FreeNullSafe( rdataPtr );
6728 CFReleaseNullSafe( collider );
6729 if( err ) exit( 1 );
6730 }
6731
6732 static void _MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
6733 {
6734 MDNSColliderRef const collider = (MDNSColliderRef) inContext;
6735
6736 CFRelease( collider );
6737 exit( inError ? 1 : 0 );
6738 }
6739
6740 //===========================================================================================================================
6741 // MDNSQueryPrintPrologue
6742 //===========================================================================================================================
6743
6744 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6745 {
6746 const int receiveSecs = inContext->receiveSecs;
6747
6748 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6749 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6750 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6751 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6752 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6753 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6754 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6755 FPrintF( stdout, "Receive duration: " );
6756 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6757 else FPrintF( stdout, "∞\n" );
6758 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6759 }
6760
6761 //===========================================================================================================================
6762 // MDNSQueryReadHandler
6763 //===========================================================================================================================
6764
6765 static void MDNSQueryReadHandler( void *inContext )
6766 {
6767 OSStatus err;
6768 struct timeval now;
6769 SocketContext * const sockCtx = (SocketContext *) inContext;
6770 MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext;
6771 size_t msgLen;
6772 sockaddr_ip fromAddr;
6773 Boolean foundAnswer = false;
6774
6775 gettimeofday( &now, NULL );
6776
6777 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6778 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6779 require_noerr( err, exit );
6780
6781 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6782 {
6783 const uint8_t * ptr;
6784 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6785 unsigned int rrCount, i;
6786 uint16_t type, class;
6787 uint8_t name[ kDomainNameLengthMax ];
6788
6789 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6790 require_noerr( err, exit );
6791
6792 if( context->qname[ 0 ] == 0 )
6793 {
6794 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6795 require_noerr( err, exit );
6796 }
6797
6798 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6799 for( i = 0; i < rrCount; ++i )
6800 {
6801 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6802 require_noerr( err, exit );
6803
6804 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6805 DomainNameEqual( name, context->qname ) )
6806 {
6807 foundAnswer = true;
6808 break;
6809 }
6810 }
6811 }
6812 if( context->allResponses || foundAnswer )
6813 {
6814 FPrintF( stdout, "---\n" );
6815 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
6816 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6817 FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6818 msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
6819 }
6820
6821 exit:
6822 if( err ) exit( 1 );
6823 }
6824
6825 //===========================================================================================================================
6826 // PIDToUUIDCmd
6827 //===========================================================================================================================
6828
6829 static void PIDToUUIDCmd( void )
6830 {
6831 OSStatus err;
6832 int n;
6833 struct proc_uniqidentifierinfo info;
6834
6835 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6836 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6837
6838 FPrintF( stdout, "%#U\n", info.p_uuid );
6839 err = kNoErr;
6840
6841 exit:
6842 if( err ) exit( 1 );
6843 }
6844
6845 //===========================================================================================================================
6846 // DNSServerCmd
6847 //===========================================================================================================================
6848
6849 typedef struct DNSServerPrivate * DNSServerRef;
6850
6851 typedef struct
6852 {
6853 DNSServerRef server; // Reference to the DNS server.
6854 dispatch_source_t sigIntSource; // Dispatch SIGINT source.
6855 dispatch_source_t sigTermSource; // Dispatch SIGTERM source.
6856 const char * domainOverride; // If non-NULL, the server is to use this domain instead of "d.test.".
6857 #if( TARGET_OS_DARWIN )
6858 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
6859 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
6860 Boolean addedResolver; // True if system DNS settings contains a resolver entry for server.
6861 #endif
6862 Boolean loopbackOnly; // True if the server should be bound to the loopback interface.
6863
6864 } DNSServerCmdContext;
6865
6866 typedef enum
6867 {
6868 kDNSServerEvent_Started = 1,
6869 kDNSServerEvent_Stopped = 2
6870
6871 } DNSServerEventType;
6872
6873 typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6874
6875 CFTypeID DNSServerGetTypeID( void );
6876 static OSStatus
6877 DNSServerCreate(
6878 dispatch_queue_t inQueue,
6879 DNSServerEventHandler_f inEventHandler,
6880 void * inEventContext,
6881 unsigned int inResponseDelayMs,
6882 uint32_t inDefaultTTL,
6883 int inPort,
6884 Boolean inLoopbackOnly,
6885 const char * inDomain,
6886 Boolean inBadUDPMode,
6887 DNSServerRef * outServer );
6888 static void DNSServerStart( DNSServerRef inServer );
6889 static void DNSServerStop( DNSServerRef inServer );
6890
6891 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6892
6893 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext );
6894 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext );
6895 static void DNSServerCmdSigIntHandler( void *inContext );
6896 static void DNSServerCmdSigTermHandler( void *inContext );
6897 #if( TARGET_OS_DARWIN )
6898 static void DNSServerCmdFollowedProcessHandler( void *inContext );
6899 #endif
6900
6901 ulog_define_ex( "com.apple.dnssdutil", DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
6902 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6903
6904 static void DNSServerCmd( void )
6905 {
6906 OSStatus err;
6907 DNSServerCmdContext * context = NULL;
6908
6909 if( gDNSServer_Foreground )
6910 {
6911 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6912 }
6913
6914 err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
6915 require_noerr_quiet( err, exit );
6916
6917 err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
6918 require_noerr_quiet( err, exit );
6919
6920 err = CheckIntegerArgument( gDNSServer_Port, "port number", -UINT16_MAX, UINT16_MAX );
6921 require_noerr_quiet( err, exit );
6922
6923 context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
6924 require_action( context, exit, err = kNoMemoryErr );
6925
6926 context->domainOverride = gDNSServer_DomainOverride;
6927 context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
6928
6929 #if( TARGET_OS_DARWIN )
6930 if( gDNSServer_FollowPID )
6931 {
6932 err = StringToPID( gDNSServer_FollowPID, &context->followPID );
6933 if( err || ( context->followPID < 0 ) )
6934 {
6935 FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
6936 err = kParamErr;
6937 goto exit;
6938 }
6939
6940 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
6941 DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
6942 require_noerr( err, exit );
6943 dispatch_resume( context->processMonitor );
6944 }
6945 else
6946 {
6947 context->followPID = -1;
6948 }
6949 #endif
6950
6951 signal( SIGINT, SIG_IGN );
6952 err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
6953 require_noerr( err, exit );
6954 dispatch_resume( context->sigIntSource );
6955
6956 signal( SIGTERM, SIG_IGN );
6957 err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
6958 require_noerr( err, exit );
6959 dispatch_resume( context->sigTermSource );
6960
6961 err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context,
6962 (unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, gDNSServer_Port, context->loopbackOnly,
6963 context->domainOverride, gDNSServer_BadUDPMode ? true : false, &context->server );
6964 require_noerr( err, exit );
6965
6966 DNSServerStart( context->server );
6967 dispatch_main();
6968
6969 exit:
6970 FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
6971 if( context ) DNSServerCmdContextFree( context );
6972 if( err ) exit( 1 );
6973 }
6974
6975 //===========================================================================================================================
6976 // DNSServerCmdContextFree
6977 //===========================================================================================================================
6978
6979 static void DNSServerCmdContextFree( DNSServerCmdContext *inContext )
6980 {
6981 ForgetCF( &inContext->server );
6982 dispatch_source_forget( &inContext->sigIntSource );
6983 dispatch_source_forget( &inContext->sigTermSource );
6984 #if( TARGET_OS_DARWIN )
6985 dispatch_source_forget( &inContext->processMonitor );
6986 #endif
6987 free( inContext );
6988 }
6989
6990 //===========================================================================================================================
6991 // DNSServerCmdEventHandler
6992 //===========================================================================================================================
6993
6994 #if( TARGET_OS_DARWIN )
6995 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort );
6996 static OSStatus _DNSServerCmdLoopbackResolverRemove( void );
6997 #endif
6998
6999 static void DNSServerCmdEventHandler( DNSServerEventType inType, uintptr_t inEventData, void *inContext )
7000 {
7001 OSStatus err;
7002 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7003
7004 if( inType == kDNSServerEvent_Started )
7005 {
7006 #if( TARGET_OS_DARWIN )
7007 const int port = (int) inEventData;
7008
7009 err = _DNSServerCmdLoopbackResolverAdd( context->domainOverride ? context->domainOverride : "d.test.", port );
7010 if( err )
7011 {
7012 ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7013 err );
7014 if( context->loopbackOnly ) ForgetDNSServer( &context->server );
7015 }
7016 else
7017 {
7018 context->addedResolver = true;
7019 }
7020 #endif
7021 }
7022 else if( inType == kDNSServerEvent_Stopped )
7023 {
7024 const OSStatus stopError = (OSStatus) inEventData;
7025
7026 if( stopError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", stopError );
7027
7028 err = kNoErr;
7029 #if( TARGET_OS_DARWIN )
7030 if( context->addedResolver )
7031 {
7032 err = _DNSServerCmdLoopbackResolverRemove();
7033 if( err )
7034 {
7035 ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7036 }
7037 else
7038 {
7039 context->addedResolver = false;
7040 }
7041 }
7042 else if( context->loopbackOnly )
7043 {
7044 err = kUnknownErr;
7045 }
7046 #endif
7047 DNSServerCmdContextFree( context );
7048 exit( ( stopError || err ) ? 1 : 0 );
7049 }
7050 }
7051
7052 #if( TARGET_OS_DARWIN )
7053 //===========================================================================================================================
7054 // _DNSServerCmdLoopbackResolverAdd
7055 //===========================================================================================================================
7056
7057 static OSStatus _DNSServerCmdLoopbackResolverAdd( const char *inDomain, int inPort )
7058 {
7059 OSStatus err;
7060 SCDynamicStoreRef store;
7061 CFPropertyListRef plist = NULL;
7062 CFStringRef key = NULL;
7063 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7064 Boolean success;
7065
7066 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
7067 err = map_scerror( store );
7068 require_noerr( err, exit );
7069
7070 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7071 "{"
7072 "%kO="
7073 "["
7074 "%s"
7075 "]"
7076 "%kO="
7077 "["
7078 "%.4a"
7079 "%.16a"
7080 "]"
7081 "%kO=%i"
7082 "}",
7083 kSCPropNetDNSSupplementalMatchDomains, inDomain,
7084 kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr,
7085 kSCPropNetDNSServerPort, inPort );
7086 require_noerr( err, exit );
7087
7088 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7089 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7090 require_action( key, exit, err = kUnknownErr );
7091
7092 success = SCDynamicStoreSetValue( store, key, plist );
7093 require_action( success, exit, err = kUnknownErr );
7094
7095 exit:
7096 CFReleaseNullSafe( store );
7097 CFReleaseNullSafe( plist );
7098 CFReleaseNullSafe( key );
7099 return( err );
7100 }
7101
7102 //===========================================================================================================================
7103 // _DNSServerCmdLoopbackResolverRemove
7104 //===========================================================================================================================
7105
7106 static OSStatus _DNSServerCmdLoopbackResolverRemove( void )
7107 {
7108 OSStatus err;
7109 SCDynamicStoreRef store;
7110 CFStringRef key = NULL;
7111 Boolean success;
7112
7113 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
7114 err = map_scerror( store );
7115 require_noerr( err, exit );
7116
7117 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
7118 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
7119 require_action( key, exit, err = kUnknownErr );
7120
7121 success = SCDynamicStoreRemoveValue( store, key );
7122 require_action( success, exit, err = kUnknownErr );
7123
7124 exit:
7125 CFReleaseNullSafe( store );
7126 CFReleaseNullSafe( key );
7127 return( err );
7128 }
7129 #endif
7130
7131 //===========================================================================================================================
7132 // DNSServerCmdSigIntHandler
7133 //===========================================================================================================================
7134
7135 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal );
7136
7137 static void DNSServerCmdSigIntHandler( void *inContext )
7138 {
7139 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGINT );
7140 }
7141
7142 //===========================================================================================================================
7143 // DNSServerCmdSigTermHandler
7144 //===========================================================================================================================
7145
7146 static void DNSServerCmdSigTermHandler( void *inContext )
7147 {
7148 _DNSServerCmdShutdown( (DNSServerCmdContext *) inContext, SIGTERM );
7149 }
7150
7151 #if( TARGET_OS_DARWIN )
7152 //===========================================================================================================================
7153 // DNSServerCmdFollowedProcessHandler
7154 //===========================================================================================================================
7155
7156 static void DNSServerCmdFollowedProcessHandler( void *inContext )
7157 {
7158 DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext;
7159
7160 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( context, 0 );
7161 }
7162 #endif
7163
7164 //===========================================================================================================================
7165 // _DNSServerCmdExternalExit
7166 //===========================================================================================================================
7167
7168 #define SignalNumberToString( X ) ( \
7169 ( (X) == SIGINT ) ? "SIGINT" : \
7170 ( (X) == SIGTERM ) ? "SIGTERM" : \
7171 "???" )
7172
7173 static void _DNSServerCmdShutdown( DNSServerCmdContext *inContext, int inSignal )
7174 {
7175 dispatch_source_forget( &inContext->sigIntSource );
7176 dispatch_source_forget( &inContext->sigTermSource );
7177 #if( TARGET_OS_DARWIN )
7178 dispatch_source_forget( &inContext->processMonitor );
7179
7180 if( inSignal == 0 )
7181 {
7182 ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
7183 }
7184 else
7185 #endif
7186 {
7187 ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
7188 }
7189
7190 ForgetDNSServer( &inContext->server );
7191 }
7192
7193 //===========================================================================================================================
7194 // DNSServerCreate
7195 //===========================================================================================================================
7196
7197 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7198
7199 typedef struct DNSDelayedResponse DNSDelayedResponse;
7200 struct DNSDelayedResponse
7201 {
7202 DNSDelayedResponse * next;
7203 sockaddr_ip destAddr;
7204 uint64_t targetTicks;
7205 uint8_t * msgPtr;
7206 size_t msgLen;
7207 };
7208
7209 struct DNSServerPrivate
7210 {
7211 CFRuntimeBase base; // CF object base.
7212 uint8_t * domain; // Parent domain of server's resource records.
7213 dispatch_queue_t queue; // Queue for DNS server's events.
7214 dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket.
7215 dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket.
7216 dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket.
7217 dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket.
7218 SocketRef sockUDPv4;
7219 SocketRef sockUDPv6;
7220 DNSServerEventHandler_f eventHandler;
7221 void * eventContext;
7222 DNSDelayedResponse * responseList;
7223 dispatch_source_t responseTimer;
7224 unsigned int responseDelayMs;
7225 uint32_t defaultTTL;
7226 uint32_t serial; // Serial number for SOA record.
7227 int port; // Port to use for receiving and sending DNS messages.
7228 OSStatus stopError;
7229 Boolean stopped;
7230 Boolean loopbackOnly;
7231 Boolean badUDPMode; // True if the server runs in Bad UDP mode.
7232 };
7233
7234 static void _DNSServerUDPReadHandler( void *inContext );
7235 static void _DNSServerTCPReadHandler( void *inContext );
7236 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse );
7237 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList );
7238
7239 CF_CLASS_DEFINE( DNSServer );
7240
7241 static OSStatus
7242 DNSServerCreate(
7243 dispatch_queue_t inQueue,
7244 DNSServerEventHandler_f inEventHandler,
7245 void * inEventContext,
7246 unsigned int inResponseDelayMs,
7247 uint32_t inDefaultTTL,
7248 int inPort,
7249 Boolean inLoopbackOnly,
7250 const char * inDomain,
7251 Boolean inBadUDPMode,
7252 DNSServerRef * outServer )
7253 {
7254 OSStatus err;
7255 DNSServerRef obj = NULL;
7256
7257 require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
7258
7259 CF_OBJECT_CREATE( DNSServer, obj, err, exit );
7260
7261 ReplaceDispatchQueue( &obj->queue, inQueue );
7262 obj->eventHandler = inEventHandler;
7263 obj->eventContext = inEventContext;
7264 obj->responseDelayMs = inResponseDelayMs;
7265 obj->defaultTTL = inDefaultTTL;
7266 obj->port = inPort;
7267 obj->loopbackOnly = inLoopbackOnly;
7268 obj->badUDPMode = inBadUDPMode;
7269
7270 if( inDomain )
7271 {
7272 err = StringToDomainName( inDomain, &obj->domain, NULL );
7273 require_noerr_quiet( err, exit );
7274 }
7275 else
7276 {
7277 err = DomainNameDup( kDDotTestDomainName, &obj->domain, NULL );
7278 require_noerr_quiet( err, exit );
7279 }
7280
7281 *outServer = obj;
7282 obj = NULL;
7283 err = kNoErr;
7284
7285 exit:
7286 CFReleaseNullSafe( obj );
7287 return( err );
7288 }
7289
7290 //===========================================================================================================================
7291 // _DNSServerFinalize
7292 //===========================================================================================================================
7293
7294 static void _DNSServerFinalize( CFTypeRef inObj )
7295 {
7296 DNSServerRef const me = (DNSServerRef) inObj;
7297
7298 check( !me->readSourceUDPv4 );
7299 check( !me->readSourceUDPv6 );
7300 check( !me->readSourceTCPv4 );
7301 check( !me->readSourceTCPv6 );
7302 check( !me->responseTimer );
7303 ForgetMem( &me->domain );
7304 dispatch_forget( &me->queue );
7305 }
7306
7307 //===========================================================================================================================
7308 // DNSServerStart
7309 //===========================================================================================================================
7310
7311 static void _DNSServerStart( void *inContext );
7312 static void _DNSServerStop( void *inContext, OSStatus inError );
7313
7314 static void DNSServerStart( DNSServerRef me )
7315 {
7316 CFRetain( me );
7317 dispatch_async_f( me->queue, me, _DNSServerStart );
7318 }
7319
7320 static void _DNSServerStart( void *inContext )
7321 {
7322 OSStatus err;
7323 struct timeval now;
7324 DNSServerRef const me = (DNSServerRef) inContext;
7325 SocketRef sock = kInvalidSocketRef;
7326 SocketContext * sockCtx = NULL;
7327 const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK );
7328 int year, month, day;
7329
7330 // Create IPv4 UDP socket.
7331 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7332 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7333 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7334 // used for the remaining sockets.
7335
7336 err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
7337 me->port, &me->port, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7338 require_noerr( err, exit );
7339 check( me->port > 0 );
7340
7341 // Create read source for IPv4 UDP socket.
7342
7343 err = SocketContextCreate( sock, me, &sockCtx );
7344 require_noerr( err, exit );
7345 sock = kInvalidSocketRef;
7346
7347 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7348 &me->readSourceUDPv4 );
7349 require_noerr( err, exit );
7350 dispatch_resume( me->readSourceUDPv4 );
7351 me->sockUDPv4 = sockCtx->sock;
7352 sockCtx = NULL;
7353
7354 // Create IPv6 UDP socket.
7355
7356 err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
7357 me->port, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
7358 require_noerr( err, exit );
7359
7360 // Create read source for IPv6 UDP socket.
7361
7362 err = SocketContextCreate( sock, me, &sockCtx );
7363 require_noerr( err, exit );
7364 sock = kInvalidSocketRef;
7365
7366 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
7367 &me->readSourceUDPv6 );
7368 require_noerr( err, exit );
7369 dispatch_resume( me->readSourceUDPv6 );
7370 me->sockUDPv6 = sockCtx->sock;
7371 sockCtx = NULL;
7372
7373 // Create IPv4 TCP socket.
7374
7375 err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
7376 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7377 require_noerr( err, exit );
7378
7379 // Create read source for IPv4 TCP socket.
7380
7381 err = SocketContextCreate( sock, me, &sockCtx );
7382 require_noerr( err, exit );
7383 sock = kInvalidSocketRef;
7384
7385 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7386 &me->readSourceTCPv4 );
7387 require_noerr( err, exit );
7388 dispatch_resume( me->readSourceTCPv4 );
7389 sockCtx = NULL;
7390
7391 // Create IPv6 TCP socket.
7392
7393 err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
7394 me->port, NULL, kSocketBufferSize_DontSet, false, &sock );
7395 require_noerr( err, exit );
7396
7397 // Create read source for IPv6 TCP socket.
7398
7399 err = SocketContextCreate( sock, me, &sockCtx );
7400 require_noerr( err, exit );
7401 sock = kInvalidSocketRef;
7402
7403 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
7404 &me->readSourceTCPv6 );
7405 require_noerr( err, exit );
7406 dispatch_resume( me->readSourceTCPv6 );
7407 sockCtx = NULL;
7408
7409 ds_ulog( kLogLevelInfo, "Server is using port %d.\n", me->port );
7410 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, (uintptr_t) me->port, me->eventContext );
7411
7412 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7413 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7414
7415 gettimeofday( &now, NULL );
7416 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
7417 NULL, NULL, NULL );
7418 me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
7419
7420 exit:
7421 ForgetSocket( &sock );
7422 if( sockCtx ) SocketContextRelease( sockCtx );
7423 if( err ) _DNSServerStop( me, err );
7424 }
7425
7426 //===========================================================================================================================
7427 // DNSServerStop
7428 //===========================================================================================================================
7429
7430 static void _DNSServerUserStop( void *inContext );
7431 static void _DNSServerStop2( void *inContext );
7432
7433 static void DNSServerStop( DNSServerRef me )
7434 {
7435 CFRetain( me );
7436 dispatch_async_f( me->queue, me, _DNSServerUserStop );
7437 }
7438
7439 static void _DNSServerUserStop( void *inContext )
7440 {
7441 DNSServerRef const me = (DNSServerRef) inContext;
7442
7443 _DNSServerStop( me, kNoErr );
7444 CFRelease( me );
7445 }
7446
7447 static void _DNSServerStop( void *inContext, OSStatus inError )
7448 {
7449 DNSServerRef const me = (DNSServerRef) inContext;
7450
7451 me->stopError = inError;
7452 dispatch_source_forget( &me->readSourceUDPv4 );
7453 dispatch_source_forget( &me->readSourceUDPv6 );
7454 dispatch_source_forget( &me->readSourceTCPv4 );
7455 dispatch_source_forget( &me->readSourceTCPv6 );
7456 dispatch_source_forget( &me->responseTimer );
7457 me->sockUDPv4 = kInvalidSocketRef;
7458 me->sockUDPv6 = kInvalidSocketRef;
7459
7460 if( me->responseList )
7461 {
7462 _DNSDelayedResponseFreeList( me->responseList );
7463 me->responseList = NULL;
7464 }
7465 dispatch_async_f( me->queue, me, _DNSServerStop2 );
7466 }
7467
7468 static void _DNSServerStop2( void *inContext )
7469 {
7470 DNSServerRef const me = (DNSServerRef) inContext;
7471
7472 if( !me->stopped )
7473 {
7474 me->stopped = true;
7475 if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, (uintptr_t) me->stopError, me->eventContext );
7476 CFRelease( me );
7477 }
7478 CFRelease( me );
7479 }
7480
7481 //===========================================================================================================================
7482 // _DNSDelayedResponseFree
7483 //===========================================================================================================================
7484
7485 static void _DNSDelayedResponseFree( DNSDelayedResponse *inResponse )
7486 {
7487 ForgetMem( &inResponse->msgPtr );
7488 free( inResponse );
7489 }
7490
7491 //===========================================================================================================================
7492 // _DNSDelayedResponseFreeList
7493 //===========================================================================================================================
7494
7495 static void _DNSDelayedResponseFreeList( DNSDelayedResponse *inList )
7496 {
7497 DNSDelayedResponse * response;
7498
7499 while( ( response = inList ) != NULL )
7500 {
7501 inList = response->next;
7502 _DNSDelayedResponseFree( response );
7503 }
7504 }
7505
7506 //===========================================================================================================================
7507 // _DNSServerUDPReadHandler
7508 //===========================================================================================================================
7509
7510 static OSStatus
7511 _DNSServerAnswerQuery(
7512 DNSServerRef inServer,
7513 const uint8_t * inQueryPtr,
7514 size_t inQueryLen,
7515 Boolean inForTCP,
7516 uint8_t ** outResponsePtr,
7517 size_t * outResponseLen );
7518
7519 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7520 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7521
7522 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7523 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7524
7525 static OSStatus
7526 _DNSServerScheduleDelayedResponse(
7527 DNSServerRef inServer,
7528 const struct sockaddr * inDestAddr,
7529 uint8_t * inMsgPtr,
7530 size_t inMsgLen );
7531 static void _DNSServerUDPDelayedSend( void *inContext );
7532
7533 static void _DNSServerUDPReadHandler( void *inContext )
7534 {
7535 OSStatus err;
7536 SocketContext * const sockCtx = (SocketContext *) inContext;
7537 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
7538 struct timeval now;
7539 ssize_t n;
7540 sockaddr_ip clientAddr;
7541 socklen_t clientAddrLen;
7542 uint8_t * responsePtr = NULL; // malloc'd
7543 size_t responseLen;
7544 uint8_t msg[ 512 ];
7545
7546 gettimeofday( &now, NULL );
7547
7548 // Receive message.
7549
7550 clientAddrLen = (socklen_t) sizeof( clientAddr );
7551 n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
7552 err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
7553 require_noerr( err, exit );
7554
7555 ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
7556
7557 if( n < kDNSHeaderLength )
7558 {
7559 ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
7560 goto exit;
7561 }
7562
7563 ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
7564
7565 // Create response.
7566
7567 err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, &responsePtr, &responseLen );
7568 require_noerr_quiet( err, exit );
7569
7570 // Schedule response.
7571
7572 if( me->responseDelayMs > 0 )
7573 {
7574 err = _DNSServerScheduleDelayedResponse( me, &clientAddr.sa, responsePtr, responseLen );
7575 require_noerr( err, exit );
7576 responsePtr = NULL;
7577 }
7578 else
7579 {
7580 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
7581
7582 n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
7583 err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
7584 require_noerr( err, exit );
7585 }
7586
7587 exit:
7588 FreeNullSafe( responsePtr );
7589 return;
7590 }
7591
7592 static OSStatus
7593 _DNSServerScheduleDelayedResponse(
7594 DNSServerRef me,
7595 const struct sockaddr * inDestAddr,
7596 uint8_t * inMsgPtr,
7597 size_t inMsgLen )
7598 {
7599 OSStatus err;
7600 DNSDelayedResponse * response;
7601 DNSDelayedResponse ** responsePtr;
7602 DNSDelayedResponse * newResponse;
7603 uint64_t targetTicks;
7604
7605 targetTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
7606
7607 newResponse = (DNSDelayedResponse *) calloc( 1, sizeof( *newResponse ) );
7608 require_action( newResponse, exit, err = kNoMemoryErr );
7609
7610 if( !me->responseList || ( targetTicks < me->responseList->targetTicks ) )
7611 {
7612 dispatch_source_forget( &me->responseTimer );
7613
7614 err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
7615 ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, _DNSServerUDPDelayedSend,
7616 NULL, me, &me->responseTimer );
7617 require_noerr( err, exit );
7618 dispatch_resume( me->responseTimer );
7619 }
7620
7621 SockAddrCopy( inDestAddr, &newResponse->destAddr );
7622 newResponse->targetTicks = targetTicks;
7623 newResponse->msgPtr = inMsgPtr;
7624 newResponse->msgLen = inMsgLen;
7625
7626 for( responsePtr = &me->responseList; ( response = *responsePtr ) != NULL; responsePtr = &response->next )
7627 {
7628 if( newResponse->targetTicks < response->targetTicks ) break;
7629 }
7630 newResponse->next = response;
7631 *responsePtr = newResponse;
7632 newResponse = NULL;
7633 err = kNoErr;
7634
7635 exit:
7636 if( newResponse ) _DNSDelayedResponseFree( newResponse );
7637 return( err );
7638 }
7639
7640 static void _DNSServerUDPDelayedSend( void *inContext )
7641 {
7642 OSStatus err;
7643 DNSServerRef const me = (DNSServerRef) inContext;
7644 DNSDelayedResponse * response;
7645 SocketRef sock;
7646 ssize_t n;
7647 uint64_t nowTicks;
7648 uint64_t remainingNs;
7649 DNSDelayedResponse * freeList = NULL;
7650
7651 dispatch_source_forget( &me->responseTimer );
7652
7653 nowTicks = UpTicks();
7654 while( ( ( response = me->responseList ) != NULL ) && ( response->targetTicks <= nowTicks ) )
7655 {
7656 me->responseList = response->next;
7657
7658 ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7659 response->msgLen, response->msgPtr, response->msgLen );
7660
7661 sock = ( response->destAddr.sa.sa_family == AF_INET ) ? me->sockUDPv4 : me->sockUDPv6;
7662 n = sendto( sock, (char *) response->msgPtr, response->msgLen, 0, &response->destAddr.sa,
7663 SockAddrGetSize( &response->destAddr ) );
7664 err = map_socket_value_errno( sock, n == (ssize_t) response->msgLen, n );
7665 check_noerr( err );
7666
7667 response->next = freeList;
7668 freeList = response;
7669 nowTicks = UpTicks();
7670 }
7671
7672 if( response )
7673 {
7674 check( response->targetTicks > nowTicks );
7675 remainingNs = UpTicksToNanoseconds( response->targetTicks - nowTicks );
7676 if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
7677
7678 err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
7679 me->queue, _DNSServerUDPDelayedSend, NULL, me, &me->responseTimer );
7680 require_noerr( err, exit );
7681 dispatch_resume( me->responseTimer );
7682 }
7683
7684 exit:
7685 if( freeList ) _DNSDelayedResponseFreeList( freeList );
7686 }
7687
7688 //===========================================================================================================================
7689 // _DNSServerAnswerQuery
7690 //===========================================================================================================================
7691
7692 #define kLabelPrefix_Alias "alias"
7693 #define kLabelPrefix_AliasTTL "alias-ttl"
7694 #define kLabelPrefix_Count "count-"
7695 #define kLabelPrefix_Tag "tag-"
7696 #define kLabelPrefix_TTL "ttl-"
7697 #define kLabel_IPv4 "ipv4"
7698 #define kLabel_IPv6 "ipv6"
7699 #define kLabelPrefix_SRV "srv-"
7700
7701 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7702 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7703
7704 typedef struct
7705 {
7706 uint16_t priority; // Priority from SRV label.
7707 uint16_t weight; // Weight from SRV label.
7708 uint16_t port; // Port number from SRV label.
7709 uint16_t targetLen; // Total length of the target hostname labels that follow an SRV label.
7710 const uint8_t * targetPtr; // Pointer to the target hostname embedded in a domain name.
7711
7712 } ParsedSRV;
7713
7714 static OSStatus
7715 _DNSServerInitializeResponseMessage(
7716 DataBuffer * inDB,
7717 unsigned int inID,
7718 unsigned int inFlags,
7719 const uint8_t * inQName,
7720 unsigned int inQType,
7721 unsigned int inQClass );
7722 static OSStatus
7723 _DNSServerAnswerQueryDynamically(
7724 DNSServerRef inServer,
7725 const uint8_t * inQName,
7726 unsigned int inQType,
7727 unsigned int inQClass,
7728 Boolean inForTCP,
7729 DataBuffer * inDB );
7730 static Boolean
7731 _DNSServerNameIsSRVName(
7732 DNSServerRef inServer,
7733 const uint8_t * inName,
7734 const uint8_t ** outDomainPtr,
7735 size_t * outDomainLen,
7736 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
7737 size_t * outSRVCount );
7738 static Boolean
7739 _DNSServerNameIsHostname(
7740 DNSServerRef inServer,
7741 const uint8_t * inName,
7742 uint32_t * outAliasCount,
7743 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
7744 size_t * outAliasTTLCount,
7745 unsigned int * outCount,
7746 unsigned int * outRandCount,
7747 uint32_t * outTTL,
7748 Boolean * outHasA,
7749 Boolean * outHasAAAA,
7750 Boolean * outHasSOA );
7751
7752 static OSStatus
7753 _DNSServerAnswerQuery(
7754 DNSServerRef me,
7755 const uint8_t * const inQueryPtr,
7756 const size_t inQueryLen,
7757 Boolean inForTCP,
7758 uint8_t ** outResponsePtr,
7759 size_t * outResponseLen )
7760 {
7761 OSStatus err;
7762 DataBuffer dataBuf;
7763 const uint8_t * ptr;
7764 const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ];
7765 const DNSHeader * qhdr;
7766 unsigned int msgID, qflags, qtype, qclass, rflags;
7767 uint8_t qname[ kDomainNameLengthMax ];
7768
7769 DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
7770
7771 require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
7772
7773 qhdr = (const DNSHeader *) inQueryPtr;
7774 msgID = DNSHeaderGetID( qhdr );
7775 qflags = DNSHeaderGetFlags( qhdr );
7776
7777 // Minimal checking of the query message's header.
7778
7779 if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response.
7780 ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query).
7781 ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question.
7782 {
7783 err = kRequestErr;
7784 goto exit;
7785 }
7786
7787 // Get QNAME.
7788
7789 ptr = (const uint8_t *) &qhdr[ 1 ];
7790 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
7791 require_noerr( err, exit );
7792
7793 // Get QTYPE and QCLASS.
7794
7795 require_action_quiet( ( queryEnd - ptr ) >= 4, exit, err = kUnderrunErr );
7796 qtype = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr );
7797 qclass = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr );
7798 ptr += 4;
7799
7800 // Create a tentative response message.
7801
7802 rflags = kDNSHeaderFlag_Response;
7803 if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
7804 DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
7805
7806 if( me->badUDPMode && !inForTCP ) msgID = (uint16_t)( msgID + 1 );
7807 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7808 require_noerr( err, exit );
7809
7810 err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inForTCP, &dataBuf );
7811 if( err )
7812 {
7813 DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
7814 err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
7815 require_noerr( err, exit );
7816 }
7817
7818 err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
7819 require_noerr( err, exit );
7820
7821 exit:
7822 DataBuffer_Free( &dataBuf );
7823 return( err );
7824 }
7825
7826 static OSStatus
7827 _DNSServerInitializeResponseMessage(
7828 DataBuffer * inDB,
7829 unsigned int inID,
7830 unsigned int inFlags,
7831 const uint8_t * inQName,
7832 unsigned int inQType,
7833 unsigned int inQClass )
7834 {
7835 OSStatus err;
7836 DNSHeader header;
7837
7838 DataBuffer_Reset( inDB );
7839
7840 memset( &header, 0, sizeof( header ) );
7841 DNSHeaderSetID( &header, inID );
7842 DNSHeaderSetFlags( &header, inFlags );
7843 DNSHeaderSetQuestionCount( &header, 1 );
7844
7845 err = DataBuffer_Append( inDB, &header, sizeof( header ) );
7846 require_noerr( err, exit );
7847
7848 err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), (uint16_t) inQType,
7849 (uint16_t) inQClass );
7850 require_noerr( err, exit );
7851
7852 exit:
7853 return( err );
7854 }
7855
7856 static OSStatus
7857 _DNSServerAnswerQueryDynamically(
7858 DNSServerRef me,
7859 const uint8_t * const inQName,
7860 const unsigned int inQType,
7861 const unsigned int inQClass,
7862 const Boolean inForTCP,
7863 DataBuffer * const inDB )
7864 {
7865 OSStatus err;
7866 DNSHeader * hdr;
7867 unsigned int flags, rcode;
7868 uint32_t aliasCount, i;
7869 uint32_t aliasTTLs[ kMaxAliasTTLCount ];
7870 size_t aliasTTLCount;
7871 unsigned int addrCount, randCount;
7872 uint32_t ttl;
7873 ParsedSRV srvArray[ kMaxParsedSRVCount ];
7874 size_t srvCount;
7875 const uint8_t * srvDomainPtr;
7876 size_t srvDomainLen;
7877 unsigned int answerCount;
7878 Boolean notImplemented, truncated;
7879 Boolean useAliasTTLs, nameExists, nameHasA, nameHasAAAA, nameHasSRV, nameHasSOA;
7880 uint8_t namePtr[ 2 ];
7881 DNSRecordFixedFields fields;
7882
7883 answerCount = 0;
7884 truncated = false;
7885 nameExists = false;
7886 require_action_quiet( inQClass == kDNSServiceClass_IN, done, notImplemented = true );
7887
7888 notImplemented = false;
7889 aliasCount = 0;
7890 nameHasA = false;
7891 nameHasAAAA = false;
7892 nameHasSOA = false;
7893 useAliasTTLs = false;
7894 nameHasSRV = false;
7895 srvDomainLen = 0;
7896 srvCount = 0;
7897
7898 if( _DNSServerNameIsHostname( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount, &ttl,
7899 &nameHasA, &nameHasAAAA, &nameHasSOA ) )
7900 {
7901 check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
7902 check( ( addrCount >= 1 ) && ( addrCount <= 255 ) );
7903 check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
7904 check( nameHasA || nameHasAAAA );
7905
7906 if( aliasTTLCount > 0 )
7907 {
7908 aliasCount = (uint32_t) aliasTTLCount;
7909 useAliasTTLs = true;
7910 }
7911 nameExists = true;
7912 }
7913 else if( _DNSServerNameIsSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) )
7914 {
7915 nameHasSRV = true;
7916 nameExists = true;
7917 }
7918 require_quiet( nameExists, done );
7919
7920 if( aliasCount > 0 )
7921 {
7922 size_t nameOffset;
7923 uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ];
7924
7925 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7926 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7927 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7928 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7929
7930 const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) };
7931
7932 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7933
7934 nameOffset = kDNSHeaderLength;
7935
7936 for( i = aliasCount; i >= 1; --i )
7937 {
7938 size_t nameLen;
7939 size_t rdataLen;
7940 uint32_t j;
7941 uint32_t aliasTTL;
7942 uint8_t nameLabel[ 1 + kDomainLabelLengthMax ];
7943
7944 if( nameOffset <= kDNSCompressionOffsetMax )
7945 {
7946 WriteDNSCompressionPtr( namePtr, nameOffset );
7947 nameLen = sizeof( namePtr );
7948 }
7949 else
7950 {
7951 memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] );
7952 nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr );
7953 }
7954
7955 if( i >= 2 )
7956 {
7957 char * dst = (char *) &rdataLabel[ 1 ];
7958 char * const lim = (char *) &rdataLabel[ countof( rdataLabel ) ];
7959
7960 if( useAliasTTLs )
7961 {
7962 err = SNPrintF_Add( &dst, lim, kLabelPrefix_AliasTTL );
7963 require_noerr( err, exit );
7964
7965 for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
7966 {
7967 err = SNPrintF_Add( &dst, lim, "-%u", aliasTTLs[ j ] );
7968 require_noerr( err, exit );
7969 }
7970 }
7971 else
7972 {
7973 err = SNPrintF_Add( &dst, lim, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 );
7974 require_noerr( err, exit );
7975 }
7976 rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] );
7977 rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr );
7978 }
7979 else
7980 {
7981 rdataLen = sizeof( superPtr );
7982 }
7983
7984 if( !inForTCP )
7985 {
7986 size_t recordLen = nameLen + sizeof( fields ) + rdataLen;
7987
7988 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
7989 {
7990 truncated = true;
7991 goto done;
7992 }
7993 }
7994 ++answerCount;
7995
7996 // Set CNAME record's NAME.
7997
7998 if( nameOffset <= kDNSCompressionOffsetMax )
7999 {
8000 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8001 require_noerr( err, exit );
8002 }
8003 else
8004 {
8005 err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] );
8006 require_noerr( err, exit );
8007
8008 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8009 require_noerr( err, exit );
8010 }
8011
8012 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8013
8014 aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
8015 DNSRecordFixedFieldsSet( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL, (uint16_t) rdataLen );
8016 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8017 require_noerr( err, exit );
8018
8019 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8020
8021 nameOffset = DataBuffer_GetLen( inDB );
8022
8023 // Set CNAME record's RDATA.
8024
8025 if( i >= 2 )
8026 {
8027 err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] );
8028 require_noerr( err, exit );
8029 }
8030 err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) );
8031 require_noerr( err, exit );
8032 }
8033
8034 namePtr[ 0 ] = superPtr[ 0 ];
8035 namePtr[ 1 ] = superPtr[ 1 ];
8036 }
8037 else
8038 {
8039 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8040
8041 WriteDNSCompressionPtr( namePtr, kDNSHeaderLength );
8042 }
8043
8044 if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
8045 {
8046 uint8_t * lsb; // Pointer to the least significant byte of record data.
8047 size_t recordLen; // Length of the entire record.
8048 size_t rdataLen; // Length of record's RDATA.
8049 uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8050 uint8_t randIntegers[ 255 ]; // Array for random integers in [1, 255].
8051 const int useBadAddrs = ( me->badUDPMode && !inForTCP ) ? true : false;
8052
8053 if( inQType == kDNSServiceType_A )
8054 {
8055 const uint32_t baseAddrV4 = useBadAddrs ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
8056
8057 require_quiet( nameHasA, done );
8058
8059 rdataLen = 4;
8060 WriteBig32( rdata, baseAddrV4 );
8061 lsb = &rdata[ 3 ];
8062 }
8063 else
8064 {
8065 const uint8_t * const baseAddrV6 = useBadAddrs ? kDNSServerBadBaseAddrV6 : kDNSServerBaseAddrV6;
8066
8067 require_quiet( nameHasAAAA, done );
8068
8069 rdataLen = 16;
8070 memcpy( rdata, baseAddrV6, 16 );
8071 lsb = &rdata[ 15 ];
8072 }
8073
8074 if( randCount > 0 )
8075 {
8076 // Populate the array with all integers between 1 and <randCount>, inclusive.
8077
8078 for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
8079
8080 // Prevent dubious static analyzer warning.
8081 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8082 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8083 // for some reason.
8084
8085 require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
8086 addrCount, randCount );
8087
8088 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8089 // 1 and <randCount>, inclusive.
8090 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8091 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8092
8093 for( i = 0; i < addrCount; ++i )
8094 {
8095 uint8_t tmp;
8096 uint32_t j;
8097
8098 j = RandomRange( i, randCount - 1 );
8099 if( i != j )
8100 {
8101 tmp = randIntegers[ i ];
8102 randIntegers[ i ] = randIntegers[ j ];
8103 randIntegers[ j ] = tmp;
8104 }
8105 }
8106 }
8107
8108 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8109 for( i = 0; i < addrCount; ++i )
8110 {
8111 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8112 {
8113 truncated = true;
8114 goto done;
8115 }
8116
8117 // Set record NAME.
8118
8119 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8120 require_noerr( err, exit );
8121
8122 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8123
8124 DNSRecordFixedFieldsSet( &fields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
8125 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8126 require_noerr( err, exit );
8127
8128 // Set record RDATA.
8129
8130 *lsb = ( randCount > 0 ) ? randIntegers[ i ] : ( *lsb + 1 );
8131
8132 err = DataBuffer_Append( inDB, rdata, rdataLen );
8133 require_noerr( err, exit );
8134
8135 ++answerCount;
8136 }
8137 }
8138 else if( inQType == kDNSServiceType_SRV )
8139 {
8140 require_quiet( nameHasSRV, done );
8141
8142 DNSRecordFixedFieldsSet( &fields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
8143
8144 for( i = 0; i < srvCount; ++i )
8145 {
8146 SRVRecordDataFixedFields fieldsSRV;
8147 size_t rdataLen;
8148 size_t recordLen;
8149 const ParsedSRV * const srv = &srvArray[ i ];
8150
8151 rdataLen = sizeof( fieldsSRV ) + srvDomainLen + srv->targetLen + 1;
8152 recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen;
8153
8154 if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
8155 {
8156 truncated = true;
8157 goto done;
8158 }
8159
8160 // Append record NAME.
8161
8162 err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) );
8163 require_noerr( err, exit );
8164
8165 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8166
8167 WriteBig16( fields.rdlength, rdataLen );
8168 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
8169 require_noerr( err, exit );
8170
8171 // Append SRV RDATA.
8172
8173 SRVRecordDataFixedFieldsSet( &fieldsSRV, srv->priority, srv->weight, srv->port );
8174
8175 err = DataBuffer_Append( inDB, &fieldsSRV, sizeof( fieldsSRV ) );
8176 require_noerr( err, exit );
8177
8178 if( srv->targetLen > 0 )
8179 {
8180 err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
8181 require_noerr( err, exit );
8182 }
8183
8184 if( srvDomainLen > 0 )
8185 {
8186 err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
8187 require_noerr( err, exit );
8188 }
8189
8190 err = DataBuffer_Append( inDB, "", 1 ); // Append root label.
8191 require_noerr( err, exit );
8192
8193 ++answerCount;
8194 }
8195 }
8196 else if( inQType == kDNSServiceType_SOA )
8197 {
8198 size_t nameLen, recordLen;
8199
8200 require_quiet( nameHasSOA, done );
8201
8202 nameLen = DomainNameLength( me->domain );
8203 if( !inForTCP )
8204 {
8205 err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
8206 require_noerr( err, exit );
8207
8208 if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
8209 {
8210 truncated = true;
8211 goto done;
8212 }
8213 }
8214
8215 err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
8216 kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
8217 me->defaultTTL, NULL );
8218 require_noerr( err, exit );
8219
8220 ++answerCount;
8221 }
8222
8223 done:
8224 hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
8225 flags = DNSHeaderGetFlags( hdr );
8226 if( notImplemented )
8227 {
8228 rcode = kDNSRCode_NotImplemented;
8229 }
8230 else
8231 {
8232 flags |= kDNSHeaderFlag_AuthAnswer;
8233 if( truncated ) flags |= kDNSHeaderFlag_Truncation;
8234 rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
8235 }
8236 DNSFlagsSetRCode( flags, rcode );
8237 DNSHeaderSetFlags( hdr, flags );
8238 DNSHeaderSetAnswerCount( hdr, answerCount );
8239 err = kNoErr;
8240
8241 exit:
8242 return( err );
8243 }
8244
8245 static Boolean
8246 _DNSServerNameIsHostname(
8247 DNSServerRef me,
8248 const uint8_t * inName,
8249 uint32_t * outAliasCount,
8250 uint32_t inAliasTTLs[ kMaxAliasTTLCount ],
8251 size_t * outAliasTTLCount,
8252 unsigned int * outCount,
8253 unsigned int * outRandCount,
8254 uint32_t * outTTL,
8255 Boolean * outHasA,
8256 Boolean * outHasAAAA,
8257 Boolean * outHasSOA )
8258 {
8259 OSStatus err;
8260 const uint8_t * label;
8261 const uint8_t * nextLabel;
8262 uint32_t aliasCount = 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8263 unsigned int count = 0; // First arg from Count label. Valid values are in [1, 255].
8264 unsigned int randCount = 0; // Second arg from Count label. Valid values are in [count, 255].
8265 int32_t ttl = -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8266 size_t aliasTTLCount = 0; // Count of TTL args from Alias-TTL label.
8267 int hasTagLabel = false;
8268 int hasIPv4Label = false;
8269 int hasIPv6Label = false;
8270 int isNameValid = false;
8271
8272 for( label = inName; label[ 0 ]; label = nextLabel )
8273 {
8274 uint32_t arg;
8275
8276 nextLabel = &label[ 1 + label[ 0 ] ];
8277
8278 // Check if the first label is a valid alias TTL sequence label.
8279
8280 if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_AliasTTL ) == 0 ) )
8281 {
8282 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_AliasTTL ) ];
8283 const char * const end = (const char *) nextLabel;
8284 const char * next;
8285
8286 check( label[ 0 ] <= kDomainLabelLengthMax );
8287
8288 while( ptr < end )
8289 {
8290 if( *ptr != '-' ) break;
8291 ++ptr;
8292 err = DecimalTextToUInt32( ptr, end, &arg, &next );
8293 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8294 inAliasTTLs[ aliasTTLCount++ ] = arg;
8295 ptr = next;
8296 }
8297 if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
8298 }
8299
8300 // Check if the first label is a valid alias label.
8301
8302 else if( ( label == inName ) && ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Alias ) == 0 ) )
8303 {
8304 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Alias ) ];
8305 const char * const end = (const char *) nextLabel;
8306
8307 if( ptr < end )
8308 {
8309 if( *ptr++ != '-' ) break;
8310 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8311 if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
8312 aliasCount = arg;
8313 if( ptr != end ) break;
8314 }
8315 else
8316 {
8317 aliasCount = 1;
8318 }
8319 }
8320
8321 // Check if this label is a valid count label.
8322
8323 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Count ) == 0 )
8324 {
8325 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_Count ) ];
8326 const char * const end = (const char *) nextLabel;
8327
8328 if( count > 0 ) break; // Count cannot be specified more than once.
8329
8330 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8331 if( err || ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be in [1, 255].
8332 count = (unsigned int) arg;
8333
8334 if( ptr < end )
8335 {
8336 if( *ptr++ != '-' ) break;
8337 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8338 if( err || ( arg < (uint32_t) count ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
8339 randCount = (unsigned int) arg;
8340 if( ptr != end ) break;
8341 }
8342 }
8343
8344 // Check if this label is a valid TTL label.
8345
8346 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_TTL ) == 0 )
8347 {
8348 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_TTL ) ];
8349 const char * const end = (const char *) nextLabel;
8350
8351 if( ttl >= 0 ) break; // TTL cannot be specified more than once.
8352
8353 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8354 if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
8355 ttl = (int32_t) arg;
8356 if( ptr != end ) break;
8357 }
8358
8359 // Check if this label is a valid IPv4 label.
8360
8361 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv4 ) == 0 )
8362 {
8363 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8364 hasIPv4Label = true;
8365 }
8366
8367 // Check if this label is a valid IPv6 label.
8368
8369 else if( strnicmpx( &label[ 1 ], label[ 0 ], kLabel_IPv6 ) == 0 )
8370 {
8371 if( hasIPv4Label || hasIPv6Label ) break; // Valid names have at most one IPv4 or IPv6 label.
8372 hasIPv6Label = true;
8373 }
8374
8375 // Check if this label is a valid tag label.
8376
8377 else if( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_Tag ) == 0 )
8378 {
8379 hasTagLabel = true;
8380 }
8381
8382 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8383 // In both cases, there are no more labels to check.
8384
8385 else
8386 {
8387 if( DomainNameEqual( label, me->domain ) ) isNameValid = true;
8388 break;
8389 }
8390 }
8391 require_quiet( isNameValid, exit );
8392
8393 if( outAliasCount ) *outAliasCount = aliasCount;
8394 if( outAliasTTLCount ) *outAliasTTLCount = aliasTTLCount;
8395 if( outCount ) *outCount = ( count > 0 ) ? count : 1;
8396 if( outRandCount ) *outRandCount = randCount;
8397 if( outTTL ) *outTTL = ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
8398 if( outHasA ) *outHasA = ( hasIPv4Label || !hasIPv6Label ) ? true : false;
8399 if( outHasAAAA ) *outHasAAAA = ( hasIPv6Label || !hasIPv4Label ) ? true : false;
8400 if( outHasSOA )
8401 {
8402 *outHasSOA = ( !count && ( ttl < 0 ) && !hasIPv4Label && !hasIPv6Label && !hasTagLabel ) ? true : false;
8403 }
8404
8405 exit:
8406 return( isNameValid ? true : false );
8407 }
8408
8409 static Boolean
8410 _DNSServerNameIsSRVName(
8411 DNSServerRef me,
8412 const uint8_t * inName,
8413 const uint8_t ** outDomainPtr,
8414 size_t * outDomainLen,
8415 ParsedSRV inSRVArray[ kMaxParsedSRVCount ],
8416 size_t * outSRVCount )
8417 {
8418 OSStatus err;
8419 const uint8_t * label;
8420 const uint8_t * domainPtr;
8421 size_t domainLen;
8422 size_t srvCount;
8423 uint32_t arg;
8424 int isNameValid = false;
8425
8426 label = inName;
8427
8428 // Ensure that first label, i.e, the service label, begins with a '_' character.
8429
8430 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8431 label = NextLabel( label );
8432
8433 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8434
8435 require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
8436 label = NextLabel( label );
8437
8438 // Parse the domain name, if any.
8439
8440 domainPtr = label;
8441 while( *label )
8442 {
8443 if( DomainNameEqual( label, me->domain ) ||
8444 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8445 label = NextLabel( label );
8446 }
8447 require_quiet( *label, exit );
8448
8449 domainLen = (size_t)( label - domainPtr );
8450
8451 // Parse SRV labels, if any.
8452
8453 srvCount = 0;
8454 while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
8455 {
8456 const uint8_t * const nextLabel = NextLabel( label );
8457 const char * ptr = (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
8458 const char * const end = (const char *) nextLabel;
8459 const uint8_t * target;
8460 unsigned int priority, weight, port;
8461
8462 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8463 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8464 priority = (unsigned int) arg;
8465
8466 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8467 ++ptr;
8468
8469 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8470 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8471 weight = (unsigned int) arg;
8472
8473 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
8474 ++ptr;
8475
8476 err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
8477 require_quiet( !err && ( arg <= UINT16_MAX ), exit );
8478 port = (unsigned int) arg;
8479
8480 require_quiet( ptr == end, exit );
8481
8482 target = nextLabel;
8483 for( label = nextLabel; *label; label = NextLabel( label ) )
8484 {
8485 if( DomainNameEqual( label, me->domain ) ||
8486 ( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
8487 }
8488 require_quiet( *label, exit );
8489
8490 if( inSRVArray )
8491 {
8492 inSRVArray[ srvCount ].priority = (uint16_t) priority;
8493 inSRVArray[ srvCount ].weight = (uint16_t) weight;
8494 inSRVArray[ srvCount ].port = (uint16_t) port;
8495 inSRVArray[ srvCount ].targetPtr = target;
8496 inSRVArray[ srvCount ].targetLen = (uint16_t)( label - target );
8497 }
8498 ++srvCount;
8499 }
8500 require_quiet( DomainNameEqual( label, me->domain ), exit );
8501 isNameValid = true;
8502
8503 if( outDomainPtr ) *outDomainPtr = domainPtr;
8504 if( outDomainLen ) *outDomainLen = domainLen;
8505 if( outSRVCount ) *outSRVCount = srvCount;
8506
8507 exit:
8508 return( isNameValid ? true : false );
8509 }
8510
8511 //===========================================================================================================================
8512 // _DNSServerTCPReadHandler
8513 //===========================================================================================================================
8514
8515 typedef struct
8516 {
8517 DNSServerRef server; // Reference to DNS server object.
8518 sockaddr_ip clientAddr; // Client's address.
8519 dispatch_source_t readSource; // Dispatch read source for client socket.
8520 dispatch_source_t writeSource; // Dispatch write source for client socket.
8521 size_t offset; // Offset into receive buffer.
8522 void * msgPtr; // Pointer to dynamically allocated message buffer.
8523 size_t msgLen; // Length of message buffer.
8524 Boolean readSuspended; // True if the read source is currently suspended.
8525 Boolean writeSuspended; // True if the write source is currently suspended.
8526 Boolean receivedLength; // True if receiving DNS message as opposed to the message length.
8527 uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field.
8528 iovec_t iov[ 2 ]; // IO vector for writing response message.
8529 iovec_t * iovPtr; // Vector pointer for SocketWriteData().
8530 int iovCount; // Vector count for SocketWriteData().
8531
8532 } TCPConnectionContext;
8533
8534 static void TCPConnectionStop( TCPConnectionContext *inContext );
8535 static void TCPConnectionContextFree( TCPConnectionContext *inContext );
8536 static void TCPConnectionReadHandler( void *inContext );
8537 static void TCPConnectionWriteHandler( void *inContext );
8538
8539 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8540
8541 static void _DNSServerTCPReadHandler( void *inContext )
8542 {
8543 OSStatus err;
8544 SocketContext * const sockCtx = (SocketContext *) inContext;
8545 DNSServerRef const me = (DNSServerRef) sockCtx->userContext;
8546 TCPConnectionContext * connection;
8547 socklen_t clientAddrLen;
8548 SocketRef newSock = kInvalidSocketRef;
8549 SocketContext * newSockCtx = NULL;
8550
8551 connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) );
8552 require_action( connection, exit, err = kNoMemoryErr );
8553
8554 CFRetain( me );
8555 connection->server = me;
8556
8557 clientAddrLen = (socklen_t) sizeof( connection->clientAddr );
8558 newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen );
8559 err = map_socket_creation_errno( newSock );
8560 require_noerr( err, exit );
8561
8562 err = SocketContextCreate( newSock, connection, &newSockCtx );
8563 require_noerr( err, exit );
8564 newSock = kInvalidSocketRef;
8565
8566 err = DispatchReadSourceCreate( newSockCtx->sock, me->queue, TCPConnectionReadHandler, SocketContextCancelHandler,
8567 newSockCtx, &connection->readSource );
8568 require_noerr( err, exit );
8569 SocketContextRetain( newSockCtx );
8570 dispatch_resume( connection->readSource );
8571
8572 err = DispatchWriteSourceCreate( newSockCtx->sock, me->queue, TCPConnectionWriteHandler, SocketContextCancelHandler,
8573 newSockCtx, &connection->writeSource );
8574 require_noerr( err, exit );
8575 SocketContextRetain( newSockCtx );
8576 connection->writeSuspended = true;
8577 connection = NULL;
8578
8579 exit:
8580 ForgetSocket( &newSock );
8581 SocketContextRelease( newSockCtx );
8582 TCPConnectionForget( &connection );
8583 }
8584
8585 //===========================================================================================================================
8586 // TCPConnectionStop
8587 //===========================================================================================================================
8588
8589 static void TCPConnectionStop( TCPConnectionContext *inContext )
8590 {
8591 dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended );
8592 dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended );
8593 }
8594
8595 //===========================================================================================================================
8596 // TCPConnectionContextFree
8597 //===========================================================================================================================
8598
8599 static void TCPConnectionContextFree( TCPConnectionContext *inContext )
8600 {
8601 check( !inContext->readSource );
8602 check( !inContext->writeSource );
8603 ForgetCF( &inContext->server );
8604 ForgetMem( &inContext->msgPtr );
8605 free( inContext );
8606 }
8607
8608 //===========================================================================================================================
8609 // TCPConnectionReadHandler
8610 //===========================================================================================================================
8611
8612 static void TCPConnectionReadHandler( void *inContext )
8613 {
8614 OSStatus err;
8615 SocketContext * const sockCtx = (SocketContext *) inContext;
8616 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8617 struct timeval now;
8618 uint8_t * responsePtr = NULL; // malloc'd
8619 size_t responseLen;
8620
8621 // Receive message length.
8622
8623 if( !connection->receivedLength )
8624 {
8625 err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset );
8626 if( err == EWOULDBLOCK ) goto exit;
8627 require_noerr( err, exit );
8628
8629 connection->offset = 0;
8630 connection->msgLen = ReadBig16( connection->lenBuf );
8631 connection->msgPtr = malloc( connection->msgLen );
8632 require_action( connection->msgPtr, exit, err = kNoMemoryErr );
8633 connection->receivedLength = true;
8634 }
8635
8636 // Receive message.
8637
8638 err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset );
8639 if( err == EWOULDBLOCK ) goto exit;
8640 require_noerr( err, exit );
8641
8642 gettimeofday( &now, NULL );
8643 dispatch_suspend( connection->readSource );
8644 connection->readSuspended = true;
8645
8646 ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8647 connection->msgLen, &connection->clientAddr, &now );
8648
8649 if( connection->msgLen < kDNSHeaderLength )
8650 {
8651 ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength );
8652 goto exit;
8653 }
8654
8655 ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen );
8656
8657 // Create response.
8658
8659 err = _DNSServerAnswerQueryForTCP( connection->server, connection->msgPtr, connection->msgLen, &responsePtr,
8660 &responseLen );
8661 require_noerr_quiet( err, exit );
8662
8663 // Send response.
8664
8665 ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
8666
8667 free( connection->msgPtr );
8668 connection->msgPtr = responsePtr;
8669 connection->msgLen = responseLen;
8670 responsePtr = NULL;
8671
8672 check( connection->msgLen <= UINT16_MAX );
8673 WriteBig16( connection->lenBuf, connection->msgLen );
8674 connection->iov[ 0 ].iov_base = connection->lenBuf;
8675 connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf );
8676 connection->iov[ 1 ].iov_base = connection->msgPtr;
8677 connection->iov[ 1 ].iov_len = connection->msgLen;
8678
8679 connection->iovPtr = connection->iov;
8680 connection->iovCount = 2;
8681
8682 check( connection->writeSuspended );
8683 dispatch_resume( connection->writeSource );
8684 connection->writeSuspended = false;
8685
8686 exit:
8687 FreeNullSafe( responsePtr );
8688 if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection );
8689 }
8690
8691 //===========================================================================================================================
8692 // TCPConnectionWriteHandler
8693 //===========================================================================================================================
8694
8695 static void TCPConnectionWriteHandler( void *inContext )
8696 {
8697 OSStatus err;
8698 SocketContext * const sockCtx = (SocketContext *) inContext;
8699 TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext;
8700
8701 err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount );
8702 if( err == EWOULDBLOCK ) goto exit;
8703 check_noerr( err );
8704
8705 TCPConnectionForget( &connection );
8706
8707 exit:
8708 return;
8709 }
8710
8711 //===========================================================================================================================
8712 // MDNSReplierCmd
8713 //===========================================================================================================================
8714
8715 typedef struct
8716 {
8717 uint8_t * hostname; // Used as the base name for hostnames and service names.
8718 uint8_t * serviceLabel; // Label containing the base service name.
8719 unsigned int maxInstanceCount; // Maximum number of service instances and hostnames.
8720 uint64_t * bitmaps; // Array of 64-bit bitmaps for keeping track of needed responses.
8721 size_t bitmapCount; // Number of 64-bit bitmaps.
8722 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
8723 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
8724 uint32_t ifIndex; // Index of the interface to run on.
8725 unsigned int recordCountA; // Number of A records per hostname.
8726 unsigned int recordCountAAAA; // Number of AAAA records per hostname.
8727 unsigned int maxDropCount; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8728 double ucastDropRate; // Probability of dropping a unicast response.
8729 double mcastDropRate; // Probability of dropping a multicast query or response.
8730 uint8_t * dropCounters; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8731 Boolean noAdditionals; // True if responses are to not include additional records.
8732 Boolean useIPv4; // True if the replier is to use IPv4.
8733 Boolean useIPv6; // True if the replier is to use IPv6.
8734 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // Buffer for received mDNS message.
8735 #if( TARGET_OS_DARWIN )
8736 dispatch_source_t processMonitor; // Process monitor source for process being followed, if any.
8737 pid_t followPID; // PID of process being followed, if any. (If it exits, we exit).
8738 #endif
8739
8740 } MDNSReplierContext;
8741
8742 typedef struct MRResourceRecord MRResourceRecord;
8743 struct MRResourceRecord
8744 {
8745 MRResourceRecord * next; // Next item in list.
8746 uint8_t * name; // Resource record name.
8747 uint16_t type; // Resource record type.
8748 uint16_t class; // Resource record class.
8749 uint32_t ttl; // Resource record TTL.
8750 uint16_t rdlength; // Resource record data length.
8751 uint8_t * rdata; // Resource record data.
8752 const uint8_t * target; // For SRV records, pointer to target in RDATA.
8753 };
8754
8755 typedef struct MRNameOffsetItem MRNameOffsetItem;
8756 struct MRNameOffsetItem
8757 {
8758 MRNameOffsetItem * next; // Next item in list.
8759 uint16_t offset; // Offset of domain name in response message.
8760 uint8_t name[ 1 ]; // Variable-length array for domain name.
8761 };
8762
8763 #if( TARGET_OS_DARWIN )
8764 static void _MDNSReplierFollowedProcessHandler( void *inContext );
8765 #endif
8766 static void _MDNSReplierReadHandler( void *inContext );
8767 static OSStatus
8768 _MDNSReplierAnswerQuery(
8769 MDNSReplierContext * inContext,
8770 const uint8_t * inQueryPtr,
8771 size_t inQueryLen,
8772 sockaddr_ip * inSender,
8773 SocketRef inSock,
8774 unsigned int inIndex );
8775 static OSStatus
8776 _MDNSReplierAnswerListAdd(
8777 MDNSReplierContext * inContext,
8778 MRResourceRecord ** inAnswerList,
8779 unsigned int inIndex,
8780 const uint8_t * inName,
8781 unsigned int inType,
8782 unsigned int inClass );
8783 static void
8784 _MDNSReplierAnswerListRemovePTR(
8785 MRResourceRecord ** inAnswerListPtr,
8786 const uint8_t * inName,
8787 const uint8_t * inRData );
8788 static OSStatus
8789 _MDNSReplierSendOrDropResponse(
8790 MDNSReplierContext * inContext,
8791 MRResourceRecord * inAnswerList,
8792 sockaddr_ip * inQuerier,
8793 SocketRef inSock,
8794 unsigned int inIndex,
8795 Boolean inUnicast );
8796 static OSStatus
8797 _MDNSReplierCreateResponse(
8798 MDNSReplierContext * inContext,
8799 MRResourceRecord * inAnswerList,
8800 unsigned int inIndex,
8801 uint8_t ** outResponsePtr,
8802 size_t * outResponseLen );
8803 static OSStatus
8804 _MDNSReplierAppendNameToResponse(
8805 DataBuffer * inResponse,
8806 const uint8_t * inName,
8807 MRNameOffsetItem ** inNameOffsetListPtr );
8808 static Boolean
8809 _MDNSReplierServiceTypeMatch(
8810 const MDNSReplierContext * inContext,
8811 const uint8_t * inName,
8812 unsigned int * outTXTSize,
8813 unsigned int * outCount );
8814 static Boolean
8815 _MDNSReplierServiceInstanceNameMatch(
8816 const MDNSReplierContext * inContext,
8817 const uint8_t * inName,
8818 unsigned int * outIndex,
8819 unsigned int * outTXTSize,
8820 unsigned int * outCount );
8821 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
8822 static Boolean
8823 _MDNSReplierHostnameMatch(
8824 const MDNSReplierContext * inContext,
8825 const uint8_t * inName,
8826 unsigned int * outIndex );
8827 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
8828 static OSStatus
8829 _MRResourceRecordCreate(
8830 uint8_t * inName,
8831 uint16_t inType,
8832 uint16_t inClass,
8833 uint32_t inTTL,
8834 uint16_t inRDLength,
8835 uint8_t * inRData,
8836 MRResourceRecord ** outRecord );
8837 static void _MRResourceRecordFree( MRResourceRecord *inRecord );
8838 static void _MRResourceRecordFreeList( MRResourceRecord *inList );
8839 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
8840 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem );
8841 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
8842
8843 ulog_define_ex( "com.apple.dnssdutil", MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
8844 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8845
8846 static void MDNSReplierCmd( void )
8847 {
8848 OSStatus err;
8849 MDNSReplierContext * context;
8850 SocketRef sockV4 = kInvalidSocketRef;
8851 SocketRef sockV6 = kInvalidSocketRef;
8852 const char * ifname;
8853 size_t len;
8854 uint8_t name[ 1 + kDomainLabelLengthMax + 1 ];
8855 char ifnameBuf[ IF_NAMESIZE + 1 ];
8856
8857 err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
8858 require_noerr_quiet( err, exit );
8859
8860 err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
8861 require_noerr_quiet( err, exit );
8862
8863 err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
8864 require_noerr_quiet( err, exit );
8865
8866 err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
8867 require_noerr_quiet( err, exit );
8868
8869 err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
8870 require_noerr_quiet( err, exit );
8871
8872 err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
8873 require_noerr_quiet( err, exit );
8874
8875 if( gMDNSReplier_Foreground )
8876 {
8877 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8878 }
8879
8880 context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
8881 require_action( context, exit, err = kNoMemoryErr );
8882
8883 context->maxInstanceCount = (unsigned int) gMDNSReplier_MaxInstanceCount;
8884 context->recordCountA = (unsigned int) gMDNSReplier_RecordCountA;
8885 context->recordCountAAAA = (unsigned int) gMDNSReplier_RecordCountAAAA;
8886 context->maxDropCount = (unsigned int) gMDNSReplier_MaxDropCount;
8887 context->ucastDropRate = gMDNSReplier_UnicastDropRate;
8888 context->mcastDropRate = gMDNSReplier_MulticastDropRate;
8889 context->noAdditionals = gMDNSReplier_NoAdditionals ? true : false;
8890 context->useIPv4 = ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
8891 context->useIPv6 = ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
8892 context->bitmapCount = ( context->maxInstanceCount + 63 ) / 64;
8893
8894 #if( TARGET_OS_DARWIN )
8895 if( gMDNSReplier_FollowPID )
8896 {
8897 err = StringToPID( gMDNSReplier_FollowPID, &context->followPID );
8898 if( err || ( context->followPID < 0 ) )
8899 {
8900 FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
8901 goto exit;
8902 }
8903
8904 err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
8905 _MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
8906 require_noerr( err, exit );
8907 dispatch_resume( context->processMonitor );
8908 }
8909 else
8910 {
8911 context->followPID = -1;
8912 }
8913 #endif
8914
8915 if( context->maxDropCount > 0 )
8916 {
8917 context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
8918 require_action( context->dropCounters, exit, err = kNoMemoryErr );
8919 }
8920
8921 context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
8922 require_action( context->bitmaps, exit, err = kNoMemoryErr );
8923
8924 // Create the base hostname label.
8925
8926 len = strlen( gMDNSReplier_Hostname );
8927 if( context->maxInstanceCount > 1 )
8928 {
8929 unsigned int maxInstanceCount, digitCount;
8930
8931 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8932 // "-<instance index>" to the base hostname.
8933
8934 maxInstanceCount = context->maxInstanceCount;
8935 for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
8936 len += ( 3 + digitCount );
8937 }
8938
8939 if( len <= kDomainLabelLengthMax )
8940 {
8941 uint8_t * dst = &name[ 1 ];
8942 uint8_t * lim = &name[ countof( name ) ];
8943
8944 SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
8945 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8946
8947 err = DomainNameDupLower( name, &context->hostname, NULL );
8948 require_noerr( err, exit );
8949 }
8950 else
8951 {
8952 FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8953 gMDNSReplier_Hostname, context->maxInstanceCount );
8954 goto exit;
8955 }
8956
8957 // Create the service label.
8958
8959 len = strlen( gMDNSReplier_ServiceTypeTag ) + 3; // We need three extra bytes for the service type prefix "_t-".
8960 if( len <= kDomainLabelLengthMax )
8961 {
8962 uint8_t * dst = &name[ 1 ];
8963 uint8_t * lim = &name[ countof( name ) ];
8964
8965 SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
8966 name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
8967
8968 err = DomainNameDupLower( name, &context->serviceLabel, NULL );
8969 require_noerr( err, exit );
8970 }
8971 else
8972 {
8973 FPrintF( stderr, "error: Service type tag is too long.\n" );
8974 goto exit;
8975 }
8976
8977 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
8978 require_noerr_quiet( err, exit );
8979
8980 ifname = if_indextoname( context->ifIndex, ifnameBuf );
8981 require_action( ifname, exit, err = kNameErr );
8982
8983 // Set up IPv4 socket.
8984
8985 if( context->useIPv4 )
8986 {
8987 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
8988 require_noerr( err, exit );
8989 }
8990
8991 // Set up IPv6 socket.
8992
8993 if( context->useIPv6 )
8994 {
8995 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
8996 require_noerr( err, exit );
8997 }
8998
8999 // Create dispatch read sources for socket(s).
9000
9001 if( IsValidSocket( sockV4 ) )
9002 {
9003 SocketContext * sockCtx;
9004
9005 err = SocketContextCreate( sockV4, context, &sockCtx );
9006 require_noerr( err, exit );
9007 sockV4 = kInvalidSocketRef;
9008
9009 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9010 &context->readSourceV4 );
9011 if( err ) ForgetSocketContext( &sockCtx );
9012 require_noerr( err, exit );
9013
9014 dispatch_resume( context->readSourceV4 );
9015 }
9016
9017 if( IsValidSocket( sockV6 ) )
9018 {
9019 SocketContext * sockCtx;
9020
9021 err = SocketContextCreate( sockV6, context, &sockCtx );
9022 require_noerr( err, exit );
9023 sockV6 = kInvalidSocketRef;
9024
9025 err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
9026 &context->readSourceV6 );
9027 if( err ) ForgetSocketContext( &sockCtx );
9028 require_noerr( err, exit );
9029
9030 dispatch_resume( context->readSourceV6 );
9031 }
9032
9033 dispatch_main();
9034
9035 exit:
9036 ForgetSocket( &sockV4 );
9037 ForgetSocket( &sockV6 );
9038 exit( 1 );
9039 }
9040
9041 #if( TARGET_OS_DARWIN )
9042 //===========================================================================================================================
9043 // _MDNSReplierFollowedProcessHandler
9044 //===========================================================================================================================
9045
9046 static void _MDNSReplierFollowedProcessHandler( void *inContext )
9047 {
9048 MDNSReplierContext * const context = (MDNSReplierContext *) inContext;
9049
9050 if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
9051 {
9052 mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
9053 exit( 0 );
9054 }
9055 }
9056 #endif
9057
9058 //===========================================================================================================================
9059 // _MDNSReplierReadHandler
9060 //===========================================================================================================================
9061
9062 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9063
9064 static void _MDNSReplierReadHandler( void *inContext )
9065 {
9066 OSStatus err;
9067 SocketContext * const sockCtx = (SocketContext *) inContext;
9068 MDNSReplierContext * const context = (MDNSReplierContext *) sockCtx->userContext;
9069 size_t msgLen;
9070 sockaddr_ip sender;
9071 const DNSHeader * hdr;
9072 unsigned int flags, questionCount, i, j;
9073 const uint8_t * ptr;
9074 int drop, isMetaQuery;
9075
9076 err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
9077 NULL, NULL, NULL, NULL );
9078 require_noerr( err, exit );
9079
9080 if( msgLen < kDNSHeaderLength )
9081 {
9082 mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
9083 goto exit;
9084 }
9085
9086 // Perform header field checks.
9087 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9088
9089 hdr = (DNSHeader *) context->msgBuf;
9090 flags = DNSHeaderGetFlags( hdr );
9091 require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit ); // Reject responses.
9092 require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit ); // Reject opcodes other than standard query.
9093 require_quiet( DNSFlagsGetRCode( flags ) == kDNSRCode_NoError, exit ); // Reject non-zero rcodes.
9094
9095 drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
9096
9097 mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9098 msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
9099
9100 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9101
9102 questionCount = DNSHeaderGetQuestionCount( hdr );
9103 require_quiet( questionCount > 0, exit );
9104
9105 memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
9106
9107 isMetaQuery = false;
9108 ptr = (const uint8_t *) &hdr[ 1 ];
9109 for( i = 0; i < questionCount; ++i )
9110 {
9111 unsigned int count, index;
9112 uint16_t qtype, qclass;
9113 uint8_t qname[ kDomainNameLengthMax ];
9114
9115 err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
9116 require_noerr_quiet( err, exit );
9117
9118 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
9119
9120 if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
9121 _MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
9122 {
9123 if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
9124 {
9125 context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
9126 }
9127 }
9128 else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
9129 {
9130 if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
9131 {
9132 for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
9133 {
9134 if( count < 64 )
9135 {
9136 context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
9137 break;
9138 }
9139 else
9140 {
9141 context->bitmaps[ j ] = ~UINT64_C( 0 );
9142 count -= 64;
9143 }
9144 }
9145 }
9146 }
9147 else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
9148 {
9149 isMetaQuery = true;
9150 }
9151 }
9152
9153 // Attempt to answer the query message using selected record sets.
9154
9155 if( isMetaQuery )
9156 {
9157 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
9158 check_noerr( err );
9159 }
9160 if( drop ) goto exit;
9161
9162 for( i = 0; i < context->bitmapCount; ++i )
9163 {
9164 for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
9165 {
9166 const uint64_t bitmask = UINT64_C( 1 ) << j;
9167
9168 if( context->bitmaps[ i ] & bitmask )
9169 {
9170 context->bitmaps[ i ] &= ~bitmask;
9171
9172 err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
9173 ( i * 64 ) + j + 1 );
9174 check_noerr( err );
9175 }
9176 }
9177 }
9178
9179 exit:
9180 return;
9181 }
9182
9183 //===========================================================================================================================
9184 // _MDNSReplierAnswerQuery
9185 //===========================================================================================================================
9186
9187 static OSStatus
9188 _MDNSReplierAnswerQuery(
9189 MDNSReplierContext * inContext,
9190 const uint8_t * inQueryPtr,
9191 size_t inQueryLen,
9192 sockaddr_ip * inSender,
9193 SocketRef inSock,
9194 unsigned int inIndex )
9195 {
9196 OSStatus err;
9197 const DNSHeader * hdr;
9198 const uint8_t * ptr;
9199 unsigned int questionCount, answerCount, i;
9200 MRResourceRecord * ucastAnswerList = NULL;
9201 MRResourceRecord * mcastAnswerList = NULL;
9202
9203 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9204
9205 // Get answers for questions.
9206
9207 check( inQueryLen >= kDNSHeaderLength );
9208 hdr = (const DNSHeader *) inQueryPtr;
9209 questionCount = DNSHeaderGetQuestionCount( hdr );
9210
9211 ptr = (const uint8_t *) &hdr[ 1 ];
9212 for( i = 0; i < questionCount; ++i )
9213 {
9214 MRResourceRecord ** answerListPtr;
9215 uint16_t qtype, qclass;
9216 uint8_t qname[ kDomainNameLengthMax ];
9217
9218 err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
9219 require_noerr_quiet( err, exit );
9220
9221 if( qclass & kQClassUnicastResponseBit )
9222 {
9223 qclass &= ~kQClassUnicastResponseBit;
9224 answerListPtr = &ucastAnswerList;
9225 }
9226 else
9227 {
9228 answerListPtr = &mcastAnswerList;
9229 }
9230
9231 err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
9232 require_noerr( err, exit );
9233 }
9234 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9235
9236 // Suppress known answers.
9237 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9238 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9239
9240 answerCount = DNSHeaderGetAnswerCount( hdr );
9241 for( i = 0; i < answerCount; ++i )
9242 {
9243 const uint8_t * rdataPtr;
9244 const uint8_t * recordPtr;
9245 uint16_t type, class;
9246 uint8_t name[ kDomainNameLengthMax ];
9247 uint8_t instance[ kDomainNameLengthMax ];
9248
9249 recordPtr = ptr;
9250 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
9251 require_noerr_quiet( err, exit );
9252
9253 if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
9254
9255 err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
9256 require_noerr( err, exit );
9257
9258 err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
9259 require_noerr_quiet( err, exit );
9260
9261 if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
9262 if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
9263 }
9264 require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
9265
9266 // Send or drop responses.
9267
9268 if( ucastAnswerList )
9269 {
9270 err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
9271 require_noerr( err, exit );
9272 }
9273
9274 if( mcastAnswerList )
9275 {
9276 err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
9277 require_noerr( err, exit );
9278 }
9279 err = kNoErr;
9280
9281 exit:
9282 _MRResourceRecordFreeList( ucastAnswerList );
9283 _MRResourceRecordFreeList( mcastAnswerList );
9284 return( err );
9285 }
9286
9287 //===========================================================================================================================
9288 // _MDNSReplierAnswerListAdd
9289 //===========================================================================================================================
9290
9291 static OSStatus
9292 _MDNSReplierAnswerListAdd(
9293 MDNSReplierContext * inContext,
9294 MRResourceRecord ** inAnswerList,
9295 unsigned int inIndex,
9296 const uint8_t * inName,
9297 unsigned int inType,
9298 unsigned int inClass )
9299 {
9300 OSStatus err;
9301 uint8_t * recordName = NULL;
9302 uint8_t * rdataPtr = NULL;
9303 size_t rdataLen;
9304 MRResourceRecord * answer;
9305 MRResourceRecord ** answerPtr;
9306 const uint8_t * const hostname = inContext->hostname;
9307 unsigned int i;
9308 uint32_t index;
9309 unsigned int count, txtSize;
9310
9311 require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
9312 require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
9313
9314 for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9315 {
9316 if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
9317 {
9318 err = kNoErr;
9319 goto exit;
9320 }
9321 }
9322
9323 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9324 // instance count are for answering queries about service instances.
9325
9326 if( inIndex == 0 )
9327 {
9328 if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
9329 {
9330 int listHasTXT = false;
9331
9332 if( inType == kDNSServiceType_ANY )
9333 {
9334 for( answer = *inAnswerList; answer; answer = answer->next )
9335 {
9336 if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
9337 {
9338 listHasTXT = true;
9339 break;
9340 }
9341 }
9342 }
9343
9344 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9345 {
9346 err = DomainNameDupLower( inName, &recordName, NULL );
9347 require_noerr( err, exit );
9348
9349 err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
9350 require_noerr( err, exit );
9351
9352 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9353 (uint16_t) rdataLen, rdataPtr, &answer );
9354 require_noerr( err, exit );
9355 recordName = NULL;
9356 rdataPtr = NULL;
9357
9358 *answerPtr = answer;
9359 }
9360 else if( inType == kDNSServiceType_NSEC )
9361 {
9362 err = DomainNameDupLower( inName, &recordName, NULL );
9363 require_noerr( err, exit );
9364
9365 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
9366 require_noerr( err, exit );
9367
9368 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9369 (uint16_t) rdataLen, rdataPtr, &answer );
9370 require_noerr( err, exit );
9371 recordName = NULL;
9372 rdataPtr = NULL;
9373
9374 *answerPtr = answer;
9375 }
9376 }
9377 }
9378 else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
9379 {
9380 int listHasA = false;
9381 int listHasAAAA = false;
9382
9383 if( inType == kDNSServiceType_ANY )
9384 {
9385 for( answer = *inAnswerList; answer; answer = answer->next )
9386 {
9387 if( answer->type == kDNSServiceType_A )
9388 {
9389 if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
9390 }
9391 else if( answer->type == kDNSServiceType_AAAA )
9392 {
9393 if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
9394 }
9395 if( listHasA && listHasAAAA ) break;
9396 }
9397 }
9398
9399 if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
9400 {
9401 for( i = 1; i <= inContext->recordCountA; ++i )
9402 {
9403 err = DomainNameDupLower( inName, &recordName, NULL );
9404 require_noerr( err, exit );
9405
9406 rdataLen = 4;
9407 rdataPtr = (uint8_t *) malloc( rdataLen );
9408 require_action( rdataPtr, exit, err = kNoMemoryErr );
9409
9410 rdataPtr[ 0 ] = 0;
9411 WriteBig16( &rdataPtr[ 1 ], inIndex );
9412 rdataPtr[ 3 ] = (uint8_t) i;
9413
9414 err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9415 (uint16_t) rdataLen, rdataPtr, &answer );
9416 require_noerr( err, exit );
9417 recordName = NULL;
9418 rdataPtr = NULL;
9419
9420 *answerPtr = answer;
9421 answerPtr = &answer->next;
9422 }
9423 }
9424
9425 if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
9426 {
9427 for( i = 1; i <= inContext->recordCountAAAA; ++i )
9428 {
9429 err = DomainNameDupLower( inName, &recordName, NULL );
9430 require_noerr( err, exit );
9431
9432 rdataLen = 16;
9433 rdataPtr = (uint8_t *) memdup( kMDNSReplierBaseAddrV6, rdataLen );
9434 require_action( rdataPtr, exit, err = kNoMemoryErr );
9435
9436 WriteBig16( &rdataPtr[ 12 ], inIndex );
9437 rdataPtr[ 15 ] = (uint8_t) i;
9438
9439 err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9440 (uint16_t) rdataLen, rdataPtr, &answer );
9441 require_noerr( err, exit );
9442 recordName = NULL;
9443 rdataPtr = NULL;
9444
9445 *answerPtr = answer;
9446 answerPtr = &answer->next;
9447 }
9448 }
9449 else if( inType == kDNSServiceType_NSEC )
9450 {
9451 err = DomainNameDupLower( inName, &recordName, NULL );
9452 require_noerr( err, exit );
9453
9454 if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
9455 {
9456 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
9457 require_noerr( err, exit );
9458 }
9459 else if( inContext->recordCountA > 0 )
9460 {
9461 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
9462 require_noerr( err, exit );
9463 }
9464 else if( inContext->recordCountAAAA > 0 )
9465 {
9466 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
9467 require_noerr( err, exit );
9468 }
9469 else
9470 {
9471 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
9472 require_noerr( err, exit );
9473 }
9474
9475 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9476 (uint16_t) rdataLen, rdataPtr, &answer );
9477 require_noerr( err, exit );
9478 recordName = NULL;
9479 rdataPtr = NULL;
9480
9481 *answerPtr = answer;
9482 }
9483 }
9484 else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
9485 {
9486 int listHasPTR = false;
9487
9488 if( inType == kDNSServiceType_ANY )
9489 {
9490 for( answer = *inAnswerList; answer; answer = answer->next )
9491 {
9492 if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
9493 {
9494 listHasPTR = true;
9495 break;
9496 }
9497 }
9498 }
9499
9500 if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
9501 {
9502 size_t recordNameLen;
9503 uint8_t * ptr;
9504 uint8_t * lim;
9505
9506 err = DomainNameDupLower( inName, &recordName, &recordNameLen );
9507 require_noerr( err, exit );
9508
9509 rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
9510 rdataPtr = (uint8_t *) malloc( rdataLen );
9511 require_action( rdataPtr, exit, err = kNoMemoryErr );
9512
9513 lim = &rdataPtr[ rdataLen ];
9514
9515 ptr = &rdataPtr[ 1 ];
9516 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9517 ptr += hostname[ 0 ];
9518 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
9519 rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
9520
9521 check( (size_t)( lim - ptr ) >= recordNameLen );
9522 memcpy( ptr, recordName, recordNameLen );
9523 ptr += recordNameLen;
9524
9525 rdataLen = (size_t)( ptr - rdataPtr );
9526
9527 err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9528 (uint16_t) rdataLen, rdataPtr, &answer );
9529 require_noerr( err, exit );
9530 recordName = NULL;
9531 rdataPtr = NULL;
9532
9533 *answerPtr = answer;
9534 }
9535 }
9536 else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
9537 ( index == inIndex ) && ( count >= inIndex ) )
9538 {
9539 int listHasSRV = false;
9540 int listHasTXT = false;
9541
9542 if( inType == kDNSServiceType_ANY )
9543 {
9544 for( answer = *inAnswerList; answer; answer = answer->next )
9545 {
9546 if( answer->type == kDNSServiceType_SRV )
9547 {
9548 if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
9549 }
9550 else if( answer->type == kDNSServiceType_TXT )
9551 {
9552 if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
9553 }
9554 if( listHasSRV && listHasTXT ) break;
9555 }
9556 }
9557
9558 if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
9559 {
9560 SRVRecordDataFixedFields * fields;
9561 uint8_t * ptr;
9562 uint8_t * lim;
9563 uint8_t * targetPtr;
9564
9565 err = DomainNameDupLower( inName, &recordName, NULL );
9566 require_noerr( err, exit );
9567
9568 rdataLen = sizeof( SRVRecordDataFixedFields ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
9569 rdataPtr = (uint8_t *) malloc( rdataLen );
9570 require_action( rdataPtr, exit, err = kNoMemoryErr );
9571
9572 lim = &rdataPtr[ rdataLen ];
9573
9574 fields = (SRVRecordDataFixedFields *) rdataPtr;
9575 SRVRecordDataFixedFieldsSet( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
9576
9577 targetPtr = (uint8_t *) &fields[ 1 ];
9578
9579 ptr = &targetPtr[ 1 ];
9580 memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
9581 ptr += hostname[ 0 ];
9582 if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
9583 targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
9584
9585 check( (size_t)( lim - ptr ) >= kLocalNameLen );
9586 memcpy( ptr, kLocalName, kLocalNameLen );
9587 ptr += kLocalNameLen;
9588
9589 rdataLen = (size_t)( ptr - rdataPtr );
9590
9591 err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9592 (uint16_t) rdataLen, rdataPtr, &answer );
9593 require_noerr( err, exit );
9594 recordName = NULL;
9595 rdataPtr = NULL;
9596
9597 *answerPtr = answer;
9598 answerPtr = &answer->next;
9599 }
9600
9601 if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
9602 {
9603 err = DomainNameDupLower( inName, &recordName, NULL );
9604 require_noerr( err, exit );
9605
9606 rdataLen = txtSize;
9607 err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
9608 require_noerr( err, exit );
9609
9610 err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
9611 (uint16_t) rdataLen, rdataPtr, &answer );
9612 require_noerr( err, exit );
9613 recordName = NULL;
9614 rdataPtr = NULL;
9615
9616 *answerPtr = answer;
9617 }
9618 else if( inType == kDNSServiceType_NSEC )
9619 {
9620 err = DomainNameDupLower( inName, &recordName, NULL );
9621 require_noerr( err, exit );
9622
9623 err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
9624 require_noerr( err, exit );
9625
9626 err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
9627 (uint16_t) rdataLen, rdataPtr, &answer );
9628 require_noerr( err, exit );
9629 recordName = NULL;
9630 rdataPtr = NULL;
9631
9632 *answerPtr = answer;
9633 }
9634 }
9635 err = kNoErr;
9636
9637 exit:
9638 FreeNullSafe( recordName );
9639 FreeNullSafe( rdataPtr );
9640 return( err );
9641 }
9642
9643 //===========================================================================================================================
9644 // _MDNSReplierAnswerListRemovePTR
9645 //===========================================================================================================================
9646
9647 static void
9648 _MDNSReplierAnswerListRemovePTR(
9649 MRResourceRecord ** inAnswerListPtr,
9650 const uint8_t * inName,
9651 const uint8_t * inRData )
9652 {
9653 MRResourceRecord * answer;
9654 MRResourceRecord ** answerPtr;
9655
9656 for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
9657 {
9658 if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
9659 DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
9660 }
9661 if( answer )
9662 {
9663 *answerPtr = answer->next;
9664 _MRResourceRecordFree( answer );
9665 }
9666 }
9667
9668 //===========================================================================================================================
9669 // _MDNSReplierSendOrDropResponse
9670 //===========================================================================================================================
9671
9672 static OSStatus
9673 _MDNSReplierSendOrDropResponse(
9674 MDNSReplierContext * inContext,
9675 MRResourceRecord * inAnswerList,
9676 sockaddr_ip * inQuerier,
9677 SocketRef inSock,
9678 unsigned int inIndex,
9679 Boolean inUnicast )
9680 {
9681 OSStatus err;
9682 uint8_t * responsePtr = NULL;
9683 size_t responseLen;
9684 const struct sockaddr * destAddr;
9685 ssize_t n;
9686 const double dropRate = inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
9687 int drop;
9688
9689 check( inIndex <= inContext->maxInstanceCount );
9690
9691 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9692 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9693 // as opposed to those for service instance records, are never dropped.
9694
9695 drop = false;
9696 if( inIndex > 0 )
9697 {
9698 if( inContext->maxDropCount > 0 )
9699 {
9700 uint8_t * const dropCount = &inContext->dropCounters[ inIndex - 1 ];
9701
9702 if( *dropCount < inContext->maxDropCount )
9703 {
9704 if( ShouldDrop( dropRate ) ) drop = true;
9705 *dropCount += 1;
9706 }
9707 }
9708 else if( ShouldDrop( dropRate ) )
9709 {
9710 drop = true;
9711 }
9712 }
9713
9714 err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
9715 require_noerr( err, exit );
9716
9717 if( inUnicast )
9718 {
9719 destAddr = &inQuerier->sa;
9720 }
9721 else
9722 {
9723 destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9724 }
9725
9726 mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9727 drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
9728
9729 if( !drop )
9730 {
9731 n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
9732 err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
9733 require_noerr( err, exit );
9734 }
9735
9736 exit:
9737 FreeNullSafe( responsePtr );
9738 return( err );
9739 }
9740
9741 //===========================================================================================================================
9742 // _MDNSReplierCreateResponse
9743 //===========================================================================================================================
9744
9745 static OSStatus
9746 _MDNSReplierCreateResponse(
9747 MDNSReplierContext * inContext,
9748 MRResourceRecord * inAnswerList,
9749 unsigned int inIndex,
9750 uint8_t ** outResponsePtr,
9751 size_t * outResponseLen )
9752 {
9753 OSStatus err;
9754 DataBuffer responseDB;
9755 DNSHeader hdr;
9756 MRResourceRecord * answer;
9757 uint8_t * responsePtr;
9758 size_t responseLen, len;
9759 unsigned int answerCount, recordCount;
9760 MRNameOffsetItem * nameOffsetList = NULL;
9761
9762 DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
9763
9764 // The current answers in the answer list will make up the response's Answer Record Section.
9765
9766 answerCount = 0;
9767 for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
9768
9769 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9770
9771 if( !inContext->noAdditionals )
9772 {
9773 for( answer = inAnswerList; answer; answer = answer->next )
9774 {
9775 switch( answer->type )
9776 {
9777 case kDNSServiceType_PTR:
9778 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
9779 answer->class );
9780 require_noerr( err, exit );
9781
9782 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
9783 answer->class );
9784 require_noerr( err, exit );
9785 break;
9786
9787 case kDNSServiceType_SRV:
9788 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
9789 answer->class );
9790 require_noerr( err, exit );
9791
9792 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
9793 answer->class );
9794 require_noerr( err, exit );
9795
9796 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9797 answer->class );
9798 require_noerr( err, exit );
9799 break;
9800
9801 case kDNSServiceType_TXT:
9802 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9803 answer->class );
9804 require_noerr( err, exit );
9805 break;
9806
9807 case kDNSServiceType_A:
9808 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
9809 answer->class );
9810 require_noerr( err, exit );
9811
9812 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9813 answer->class );
9814 require_noerr( err, exit );
9815 break;
9816
9817 case kDNSServiceType_AAAA:
9818 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
9819 answer->class );
9820 require_noerr( err, exit );
9821
9822 err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
9823 answer->class );
9824 require_noerr( err, exit );
9825 break;
9826
9827 default:
9828 break;
9829 }
9830 }
9831 }
9832
9833 // Append a provisional header to the response message.
9834
9835 memset( &hdr, 0, sizeof( hdr ) );
9836 DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
9837
9838 err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
9839 require_noerr( err, exit );
9840
9841 // Append answers to response message.
9842
9843 responseLen = DataBuffer_GetLen( &responseDB );
9844 recordCount = 0;
9845 for( answer = inAnswerList; answer; answer = answer->next )
9846 {
9847 DNSRecordFixedFields fields;
9848 unsigned int class;
9849
9850 // Append record NAME.
9851
9852 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
9853 require_noerr( err, exit );
9854
9855 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9856
9857 class = answer->class;
9858 if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT ) ||
9859 ( answer->type == kDNSServiceType_A ) || ( answer->type == kDNSServiceType_AAAA ) ||
9860 ( answer->type == kDNSServiceType_NSEC ) )
9861 {
9862 class |= kRRClassCacheFlushBit;
9863 }
9864
9865 DNSRecordFixedFieldsSet( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
9866 err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
9867 require_noerr( err, exit );
9868
9869 // Append record RDATA.
9870 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9871
9872 if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
9873 ( answer->type == kDNSServiceType_NSEC ) )
9874 {
9875 size_t rdlength;
9876 uint8_t * rdLengthPtr;
9877 const size_t rdLengthOffset = DataBuffer_GetLen( &responseDB ) - 2;
9878 const size_t rdataOffset = DataBuffer_GetLen( &responseDB );
9879
9880 if( answer->type == kDNSServiceType_PTR )
9881 {
9882 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9883 require_noerr( err, exit );
9884 }
9885 else if( answer->type == kDNSServiceType_SRV )
9886 {
9887 require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
9888
9889 err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
9890 require_noerr( err, exit );
9891
9892 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
9893 require_noerr( err, exit );
9894 }
9895 else
9896 {
9897 const size_t nameLen = DomainNameLength( answer->rdata );
9898
9899 err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
9900 require_noerr( err, exit );
9901
9902 require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
9903
9904 err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
9905 require_noerr( err, exit );
9906 }
9907
9908 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9909
9910 rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
9911 check( rdlength <= UINT16_MAX );
9912
9913 rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
9914 WriteBig16( rdLengthPtr, rdlength );
9915 }
9916 else
9917 {
9918 err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
9919 require_noerr( err, exit );
9920 }
9921
9922 if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
9923 responseLen = DataBuffer_GetLen( &responseDB );
9924 ++recordCount;
9925 }
9926
9927 // Set the response header's Answer and Additional record counts.
9928 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9929 // response message to exceed the maximum mDNS message size.
9930
9931 if( recordCount <= answerCount )
9932 {
9933 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
9934 }
9935 else
9936 {
9937 DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
9938 DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
9939 }
9940
9941 err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
9942 require_noerr( err, exit );
9943
9944 if( outResponsePtr ) *outResponsePtr = responsePtr;
9945 if( outResponseLen ) *outResponseLen = responseLen;
9946
9947 exit:
9948 _MRNameOffsetItemFreeList( nameOffsetList );
9949 DataBuffer_Free( &responseDB );
9950 return( err );
9951 }
9952
9953 //===========================================================================================================================
9954 // _MDNSReplierAppendNameToResponse
9955 //===========================================================================================================================
9956
9957 static OSStatus
9958 _MDNSReplierAppendNameToResponse(
9959 DataBuffer * inResponse,
9960 const uint8_t * inName,
9961 MRNameOffsetItem ** inNameOffsetListPtr )
9962 {
9963 OSStatus err;
9964 const uint8_t * subname;
9965 const uint8_t * limit;
9966 size_t nameOffset;
9967 MRNameOffsetItem * item;
9968 uint8_t compressionPtr[ 2 ];
9969
9970 nameOffset = DataBuffer_GetLen( inResponse );
9971
9972 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9973
9974 for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
9975 {
9976 for( item = *inNameOffsetListPtr; item; item = item->next )
9977 {
9978 if( DomainNameEqual( item->name, subname ) ) break;
9979 }
9980
9981 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9982 // the subname's first label.
9983
9984 if( item )
9985 {
9986 WriteDNSCompressionPtr( compressionPtr, item->offset );
9987
9988 err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
9989 require_noerr( err, exit );
9990 break;
9991 }
9992 else
9993 {
9994 err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
9995 require_noerr( err, exit );
9996 }
9997 }
9998
9999 // If we made it to the root label, then no subname was able to be compressed. All of the name's labels up to the root
10000 // label were appended to the response message, so a root label is needed to terminate the complete name.
10001
10002 if( subname[ 0 ] == 0 )
10003 {
10004 err = DataBuffer_Append( inResponse, "", 1 );
10005 require_noerr( err, exit );
10006 }
10007
10008 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
10009
10010 limit = subname;
10011 for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
10012 {
10013 const size_t subnameOffset = nameOffset + (size_t)( subname - inName );
10014
10015 if( subnameOffset > kDNSCompressionOffsetMax ) break;
10016
10017 err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
10018 require_noerr( err, exit );
10019
10020 item->next = *inNameOffsetListPtr;
10021 *inNameOffsetListPtr = item;
10022 }
10023 err = kNoErr;
10024
10025 exit:
10026 return( err );
10027 }
10028
10029 //===========================================================================================================================
10030 // _MDNSReplierServiceTypeMatch
10031 //===========================================================================================================================
10032
10033 static Boolean
10034 _MDNSReplierServiceTypeMatch(
10035 const MDNSReplierContext * inContext,
10036 const uint8_t * inName,
10037 unsigned int * outTXTSize,
10038 unsigned int * outCount )
10039 {
10040 OSStatus err;
10041 const char * ptr;
10042 const char * end;
10043 uint32_t txtSize, count;
10044 const uint8_t * const serviceLabel = inContext->serviceLabel;
10045 int nameMatches = false;
10046
10047 require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
10048 if( memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
10049
10050 ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
10051 end = (const char *) &inName[ 1 + inName[ 0 ] ];
10052
10053 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10054 ++ptr;
10055
10056 err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
10057 require_noerr_quiet( err, exit );
10058 require_quiet( txtSize <= UINT16_MAX, exit );
10059
10060 require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10061 ++ptr;
10062
10063 err = DecimalTextToUInt32( ptr, end, &count, &ptr );
10064 require_noerr_quiet( err, exit );
10065 require_quiet( count <= UINT16_MAX, exit );
10066 require_quiet( ptr == end, exit );
10067
10068 if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
10069 nameMatches = true;
10070
10071 if( outTXTSize ) *outTXTSize = txtSize;
10072 if( outCount ) *outCount = count;
10073
10074 exit:
10075 return( nameMatches ? true : false );
10076 }
10077
10078 //===========================================================================================================================
10079 // _MDNSReplierServiceInstanceNameMatch
10080 //===========================================================================================================================
10081
10082 static Boolean
10083 _MDNSReplierServiceInstanceNameMatch(
10084 const MDNSReplierContext * inContext,
10085 const uint8_t * inName,
10086 unsigned int * outIndex,
10087 unsigned int * outTXTSize,
10088 unsigned int * outCount )
10089 {
10090 OSStatus err;
10091 const uint8_t * ptr;
10092 const uint8_t * end;
10093 uint32_t index;
10094 unsigned int txtSize, count;
10095 const uint8_t * const hostname = inContext->hostname;
10096 int nameMatches = false;
10097
10098 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10099 if( memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10100
10101 ptr = &inName[ 1 + hostname[ 0 ] ];
10102 end = &inName[ 1 + inName[ 0 ] ];
10103 if( ptr < end )
10104 {
10105 require_quiet( ( end - ptr ) >= 2, exit );
10106 require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
10107 ptr += 2;
10108
10109 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10110 require_noerr_quiet( err, exit );
10111 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10112
10113 require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
10114 ++ptr;
10115 }
10116 else
10117 {
10118 index = 1;
10119 }
10120
10121 if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
10122 nameMatches = true;
10123
10124 if( outIndex ) *outIndex = index;
10125 if( outTXTSize ) *outTXTSize = txtSize;
10126 if( outCount ) *outCount = count;
10127
10128 exit:
10129 return( nameMatches ? true : false );
10130 }
10131
10132 //===========================================================================================================================
10133 // _MDNSReplierAboutRecordNameMatch
10134 //===========================================================================================================================
10135
10136 static Boolean _MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
10137 {
10138 const uint8_t * subname;
10139 const uint8_t * const hostname = inContext->hostname;
10140 int nameMatches = false;
10141
10142 if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
10143 subname = NextLabel( inName );
10144
10145 if( !MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
10146 subname = NextLabel( subname );
10147
10148 if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
10149 nameMatches = true;
10150
10151 exit:
10152 return( nameMatches ? true : false );
10153 }
10154
10155 //===========================================================================================================================
10156 // _MDNSReplierHostnameMatch
10157 //===========================================================================================================================
10158
10159 static Boolean
10160 _MDNSReplierHostnameMatch(
10161 const MDNSReplierContext * inContext,
10162 const uint8_t * inName,
10163 unsigned int * outIndex )
10164 {
10165 OSStatus err;
10166 const uint8_t * ptr;
10167 const uint8_t * end;
10168 uint32_t index;
10169 const uint8_t * const hostname = inContext->hostname;
10170 int nameMatches = false;
10171
10172 require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
10173 if( memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
10174
10175 ptr = &inName[ 1 + hostname[ 0 ] ];
10176 end = &inName[ 1 + inName[ 0 ] ];
10177 if( ptr < end )
10178 {
10179 require_quiet( *ptr == '-', exit );
10180 ++ptr;
10181
10182 err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
10183 require_noerr_quiet( err, exit );
10184 require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
10185 require_quiet( ptr == end, exit );
10186 }
10187 else
10188 {
10189 index = 1;
10190 }
10191
10192 if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
10193 nameMatches = true;
10194
10195 if( outIndex ) *outIndex = index;
10196
10197 exit:
10198 return( nameMatches ? true : false );
10199 }
10200
10201 //===========================================================================================================================
10202 // _MDNSReplierCreateTXTRecord
10203 //===========================================================================================================================
10204
10205 static OSStatus _MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
10206 {
10207 OSStatus err;
10208 uint8_t * txt;
10209 uint8_t * ptr;
10210 size_t i, wholeCount, remCount;
10211 uint32_t hash;
10212 int n;
10213 uint8_t txtStr[ 16 ];
10214
10215 require_action_quiet( inSize > 0, exit, err = kSizeErr );
10216
10217 txt = (uint8_t *) malloc( inSize );
10218 require_action( txt, exit, err = kNoMemoryErr );
10219
10220 hash = FNV1( inRecordName, DomainNameLength( inRecordName ) );
10221
10222 txtStr[ 0 ] = 15;
10223 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
10224 check( n == 15 );
10225
10226 ptr = txt;
10227 wholeCount = inSize / 16;
10228 for( i = 0; i < wholeCount; ++i )
10229 {
10230 memcpy( ptr, txtStr, 16 );
10231 ptr += 16;
10232 }
10233
10234 remCount = inSize % 16;
10235 if( remCount > 0 )
10236 {
10237 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
10238 memcpy( ptr, txtStr, remCount );
10239 ptr += remCount;
10240 }
10241 check( ptr == &txt[ inSize ] );
10242
10243 *outTXT = txt;
10244 err = kNoErr;
10245
10246 exit:
10247 return( err );
10248 }
10249
10250 //===========================================================================================================================
10251 // _MRResourceRecordCreate
10252 //===========================================================================================================================
10253
10254 static OSStatus
10255 _MRResourceRecordCreate(
10256 uint8_t * inName,
10257 uint16_t inType,
10258 uint16_t inClass,
10259 uint32_t inTTL,
10260 uint16_t inRDLength,
10261 uint8_t * inRData,
10262 MRResourceRecord ** outRecord )
10263 {
10264 OSStatus err;
10265 MRResourceRecord * obj;
10266
10267 obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
10268 require_action( obj, exit, err = kNoMemoryErr );
10269
10270 obj->name = inName;
10271 obj->type = inType;
10272 obj->class = inClass;
10273 obj->ttl = inTTL;
10274 obj->rdlength = inRDLength;
10275 obj->rdata = inRData;
10276
10277 if( inType == kDNSServiceType_SRV )
10278 {
10279 require_action_quiet( obj->rdlength > sizeof( SRVRecordDataFixedFields ), exit, err = kMalformedErr );
10280 obj->target = obj->rdata + sizeof( SRVRecordDataFixedFields );
10281 }
10282
10283 *outRecord = obj;
10284 obj = NULL;
10285 err = kNoErr;
10286
10287 exit:
10288 FreeNullSafe( obj );
10289 return( err );
10290 }
10291
10292 //===========================================================================================================================
10293 // _MRResourceRecordFree
10294 //===========================================================================================================================
10295
10296 static void _MRResourceRecordFree( MRResourceRecord *inRecord )
10297 {
10298 ForgetMem( &inRecord->name );
10299 ForgetMem( &inRecord->rdata );
10300 free( inRecord );
10301 }
10302
10303 //===========================================================================================================================
10304 // _MRResourceRecordFreeList
10305 //===========================================================================================================================
10306
10307 static void _MRResourceRecordFreeList( MRResourceRecord *inList )
10308 {
10309 MRResourceRecord * record;
10310
10311 while( ( record = inList ) != NULL )
10312 {
10313 inList = record->next;
10314 _MRResourceRecordFree( record );
10315 }
10316 }
10317
10318 //===========================================================================================================================
10319 // _MRNameOffsetItemCreate
10320 //===========================================================================================================================
10321
10322 static OSStatus _MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
10323 {
10324 OSStatus err;
10325 MRNameOffsetItem * obj;
10326 size_t nameLen;
10327
10328 require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
10329
10330 nameLen = DomainNameLength( inName );
10331 obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
10332 require_action( obj, exit, err = kNoMemoryErr );
10333
10334 obj->offset = inOffset;
10335 memcpy( obj->name, inName, nameLen );
10336
10337 *outItem = obj;
10338 err = kNoErr;
10339
10340 exit:
10341 return( err );
10342 }
10343
10344 //===========================================================================================================================
10345 // _MRNameOffsetItemFree
10346 //===========================================================================================================================
10347
10348 static void _MRNameOffsetItemFree( MRNameOffsetItem *inItem )
10349 {
10350 free( inItem );
10351 }
10352
10353 //===========================================================================================================================
10354 // _MRNameOffsetItemFreeList
10355 //===========================================================================================================================
10356
10357 static void _MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
10358 {
10359 MRNameOffsetItem * item;
10360
10361 while( ( item = inList ) != NULL )
10362 {
10363 inList = item->next;
10364 _MRNameOffsetItemFree( item );
10365 }
10366 }
10367
10368 //===========================================================================================================================
10369 // GAIPerfCmd
10370 //===========================================================================================================================
10371
10372 #define kGAIPerfGAITimeLimitMs 500 // Allow at most 500 ms for a DNSServiceGetAddrInfo() operation to complete.
10373 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10374
10375 typedef struct GAITesterPrivate * GAITesterRef;
10376 typedef struct GAITestCase GAITestCase;
10377
10378 typedef struct
10379 {
10380 const char * name; // Domain name that was resolved.
10381 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
10382 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
10383 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
10384 OSStatus error;
10385
10386 } GAITestItemResult;
10387
10388 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
10389 typedef void
10390 ( *GAITesterResultsHandler_f )(
10391 const char * inCaseTitle,
10392 NanoTime64 inCaseStartTime,
10393 NanoTime64 inCaseEndTime,
10394 const GAITestItemResult * inResultArray,
10395 size_t inResultCount,
10396 void * inContext );
10397
10398 typedef unsigned int GAITestAddrType;
10399 #define kGAITestAddrType_None 0
10400 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10401 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10402 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10403
10404 #define GAITestAddrTypeIsValid( X ) \
10405 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10406
10407 typedef struct
10408 {
10409 GAITesterRef tester; // GAI tester object.
10410 CFMutableArrayRef testCaseResults; // Array of test case results.
10411 unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10412 unsigned int serverDelayMs; // Amount of additional time to have server delay its responses.
10413 unsigned int defaultIterCount; // Default test case iteration count.
10414 dispatch_source_t sigIntSource; // Dispatch source for SIGINT.
10415 dispatch_source_t sigTermSource; // Dispatch source for SIGTERM.
10416 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
10417 OutputFormatType outputFormat; // Format of test results output.
10418 Boolean appendNewline; // True if a newline character should be appended to JSON output.
10419 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10420 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
10421 Boolean testFailed; // True if at least one test case iteration failed.
10422
10423 } GAIPerfContext;
10424
10425 static void GAIPerfContextFree( GAIPerfContext *inContext );
10426 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
10427 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
10428 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
10429 static void
10430 GAIPerfResultsHandler(
10431 const char * inCaseTitle,
10432 NanoTime64 inCaseStartTime,
10433 NanoTime64 inCaseEndTime,
10434 const GAITestItemResult * inResultArray,
10435 size_t inResultCount,
10436 void * inContext );
10437 static void GAIPerfSignalHandler( void *inContext );
10438
10439 CFTypeID GAITesterGetTypeID( void );
10440 static OSStatus
10441 GAITesterCreate(
10442 dispatch_queue_t inQueue,
10443 int inCallDelayMs,
10444 int inServerDelayMs,
10445 int inServerDefaultTTL,
10446 Boolean inSkipPathEvaluation,
10447 Boolean inBadUDPMode,
10448 GAITesterRef * outTester );
10449 static void GAITesterStart( GAITesterRef inTester );
10450 static void GAITesterStop( GAITesterRef inTester );
10451 static OSStatus GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
10452 static void
10453 GAITesterSetStopHandler(
10454 GAITesterRef inTester,
10455 GAITesterStopHandler_f inEventHandler,
10456 void * inEventContext );
10457 static void
10458 GAITesterSetResultsHandler(
10459 GAITesterRef inTester,
10460 GAITesterResultsHandler_f inResultsHandler,
10461 void * inResultsContext );
10462
10463 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
10464 static void GAITestCaseFree( GAITestCase *inCase );
10465 static OSStatus
10466 GAITestCaseAddItem(
10467 GAITestCase * inCase,
10468 unsigned int inAliasCount,
10469 unsigned int inAddressCount,
10470 int inTTL,
10471 GAITestAddrType inHasAddrs,
10472 GAITestAddrType inWantAddrs,
10473 unsigned int inTimeLimitMs,
10474 unsigned int inItemCount );
10475 static OSStatus
10476 GAITestCaseAddLocalHostItem(
10477 GAITestCase * inCase,
10478 GAITestAddrType inWantAddrs,
10479 unsigned int inTimeLimitMs,
10480 unsigned int inItemCount );
10481
10482 static void GAIPerfCmd( void )
10483 {
10484 OSStatus err;
10485 GAIPerfContext * context = NULL;
10486
10487 err = CheckRootUser();
10488 require_noerr_quiet( err, exit );
10489
10490 err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
10491 require_noerr_quiet( err, exit );
10492
10493 err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
10494 require_noerr_quiet( err, exit );
10495
10496 err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
10497 require_noerr_quiet( err, exit );
10498
10499 context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
10500 require_action( context, exit, err = kNoMemoryErr );
10501
10502 context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
10503 require_action( context->testCaseResults, exit, err = kNoMemoryErr );
10504
10505 context->callDelayMs = (unsigned int) gGAIPerf_CallDelayMs;
10506 context->serverDelayMs = (unsigned int) gGAIPerf_ServerDelayMs;
10507 context->defaultIterCount = (unsigned int) gGAIPerf_IterationCount;
10508 context->appendNewline = gGAIPerf_OutputAppendNewline ? true : false;
10509 context->skipPathEval = gGAIPerf_SkipPathEvalulation ? true : false;
10510 context->badUDPMode = gGAIPerf_BadUDPMode ? true : false;
10511
10512 if( gGAIPerf_OutputFilePath )
10513 {
10514 context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
10515 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
10516 }
10517
10518 context->outputFormat = (OutputFormatType) CLIArgToValue( "format", gGAIPerf_OutputFormat, &err,
10519 kOutputFormatStr_JSON, kOutputFormatType_JSON,
10520 kOutputFormatStr_XML, kOutputFormatType_XML,
10521 kOutputFormatStr_Binary, kOutputFormatType_Binary,
10522 NULL );
10523 require_noerr_quiet( err, exit );
10524
10525 err = GAITesterCreate( dispatch_get_main_queue(), (int) context->callDelayMs, (int) context->serverDelayMs,
10526 kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
10527 require_noerr( err, exit );
10528
10529 check( gGAIPerf_TestSuite );
10530 if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
10531 {
10532 err = GAIPerfAddBasicTestCases( context );
10533 require_noerr( err, exit );
10534 }
10535 else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
10536 {
10537 err = GAIPerfAddAdvancedTestCases( context );
10538 require_noerr( err, exit );
10539 }
10540 else
10541 {
10542 FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
10543 err = kParamErr;
10544 goto exit;
10545 }
10546
10547 GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
10548 GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
10549
10550 signal( SIGINT, SIG_IGN );
10551 err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource );
10552 require_noerr( err, exit );
10553 dispatch_resume( context->sigIntSource );
10554
10555 signal( SIGTERM, SIG_IGN );
10556 err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource );
10557 require_noerr( err, exit );
10558 dispatch_resume( context->sigTermSource );
10559
10560 GAITesterStart( context->tester );
10561 dispatch_main();
10562
10563 exit:
10564 if( context ) GAIPerfContextFree( context );
10565 exit( 1 );
10566 }
10567
10568 //===========================================================================================================================
10569 // GAIPerfContextFree
10570 //===========================================================================================================================
10571
10572 static void GAIPerfContextFree( GAIPerfContext *inContext )
10573 {
10574 ForgetCF( &inContext->tester );
10575 ForgetCF( &inContext->testCaseResults );
10576 ForgetMem( &inContext->outputFilePath );
10577 dispatch_source_forget( &inContext->sigIntSource );
10578 dispatch_source_forget( &inContext->sigTermSource );
10579 free( inContext );
10580 }
10581
10582 //===========================================================================================================================
10583 // GAIPerfAddAdvancedTestCases
10584 //===========================================================================================================================
10585
10586 #define kTestCaseTitleBufferSize 128
10587
10588 static void
10589 _GAIPerfWriteTestCaseTitle(
10590 char inBuffer[ kTestCaseTitleBufferSize ],
10591 unsigned int inCNAMERecordCount,
10592 unsigned int inARecordCount,
10593 unsigned int inAAAARecordCount,
10594 GAITestAddrType inRequested,
10595 unsigned int inIterationCount,
10596 Boolean inIterationsAreUnique );
10597 static void
10598 _GAIPerfWriteLocalHostTestCaseTitle(
10599 char inBuffer[ kTestCaseTitleBufferSize ],
10600 GAITestAddrType inRequested,
10601 unsigned int inIterationCount );
10602
10603 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10604 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10605
10606 static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
10607 {
10608 OSStatus err;
10609 unsigned int aliasCount, addressCount, i;
10610 GAITestCase * testCase = NULL;
10611 char title[ kTestCaseTitleBufferSize ];
10612
10613 aliasCount = 0;
10614 while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
10615 {
10616 for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
10617 {
10618 // Add a test case to resolve a domain name with
10619 //
10620 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10621 //
10622 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10623 // requires server queries.
10624
10625 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10626 inContext->defaultIterCount, true );
10627
10628 err = GAITestCaseCreate( title, &testCase );
10629 require_noerr( err, exit );
10630
10631 for( i = 0; i < inContext->defaultIterCount; ++i )
10632 {
10633 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10634 kGAITestAddrType_Both, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs, 1 );
10635 require_noerr( err, exit );
10636 }
10637
10638 err = GAITesterAddTestCase( inContext->tester, testCase );
10639 require_noerr( err, exit );
10640 testCase = NULL;
10641
10642 // Add a test case to resolve a domain name with
10643 //
10644 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10645 //
10646 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10647 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10648 // ideally require no server queries, i.e., the results should come from the cache.
10649
10650 _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
10651 inContext->defaultIterCount, false );
10652
10653 err = GAITestCaseCreate( title, &testCase );
10654 require_noerr( err, exit );
10655
10656 err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
10657 kGAITestAddrType_Both, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs, inContext->defaultIterCount + 1 );
10658 require_noerr( err, exit );
10659
10660 err = GAITesterAddTestCase( inContext->tester, testCase );
10661 require_noerr( err, exit );
10662 testCase = NULL;
10663 }
10664
10665 aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
10666 }
10667
10668 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10669
10670 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10671
10672 err = GAITestCaseCreate( title, &testCase );
10673 require_noerr( err, exit );
10674
10675 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs,
10676 inContext->defaultIterCount );
10677 require_noerr( err, exit );
10678
10679 err = GAITesterAddTestCase( inContext->tester, testCase );
10680 require_noerr( err, exit );
10681 testCase = NULL;
10682
10683 exit:
10684 if( testCase ) GAITestCaseFree( testCase );
10685 return( err );
10686 }
10687
10688 //===========================================================================================================================
10689 // _GAIPerfWriteTestCaseTitle
10690 //===========================================================================================================================
10691
10692 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10693 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10694 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10695 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10696 "" )
10697
10698 static void
10699 _GAIPerfWriteTestCaseTitle(
10700 char inBuffer[ kTestCaseTitleBufferSize ],
10701 unsigned int inCNAMERecordCount,
10702 unsigned int inARecordCount,
10703 unsigned int inAAAARecordCount,
10704 GAITestAddrType inRequested,
10705 unsigned int inIterationCount,
10706 Boolean inIterationsAreUnique )
10707 {
10708 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10709 inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
10710 inIterationCount, inIterationsAreUnique, ",unique" );
10711 }
10712
10713 //===========================================================================================================================
10714 // _GAIPerfWriteLocalHostTestCaseTitle
10715 //===========================================================================================================================
10716
10717 static void
10718 _GAIPerfWriteLocalHostTestCaseTitle(
10719 char inBuffer[ kTestCaseTitleBufferSize ],
10720 GAITestAddrType inRequested,
10721 unsigned int inIterationCount )
10722 {
10723 SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
10724 GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
10725 }
10726
10727 //===========================================================================================================================
10728 // GAIPerfAddBasicTestCases
10729 //===========================================================================================================================
10730
10731 #define kGAIPerfBasicTestSuite_AliasCount 2
10732 #define kGAIPerfBasicTestSuite_AddrCount 4
10733
10734 static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
10735 {
10736 OSStatus err;
10737 GAITestCase * testCase = NULL;
10738 char title[ kTestCaseTitleBufferSize ];
10739 unsigned int i;
10740
10741 // Test Case #1:
10742 // Resolve a domain name with
10743 //
10744 // 2 CNAME records, 4 A records, and 4 AAAA records
10745 //
10746 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10747 // queries.
10748
10749 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10750 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10751 inContext->defaultIterCount, true );
10752
10753 err = GAITestCaseCreate( title, &testCase );
10754 require_noerr( err, exit );
10755
10756 for( i = 0; i < inContext->defaultIterCount; ++i )
10757 {
10758 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10759 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs, 1 );
10760 require_noerr( err, exit );
10761 }
10762
10763 err = GAITesterAddTestCase( inContext->tester, testCase );
10764 require_noerr( err, exit );
10765 testCase = NULL;
10766
10767 // Test Case #2:
10768 // Resolve a domain name with
10769 //
10770 // 2 CNAME records, 4 A records, and 4 AAAA records
10771 //
10772 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10773 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10774 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10775
10776 _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
10777 kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
10778 inContext->defaultIterCount, false );
10779
10780 err = GAITestCaseCreate( title, &testCase );
10781 require_noerr( err, exit );
10782
10783 err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
10784 kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs,
10785 inContext->defaultIterCount + 1 );
10786 require_noerr( err, exit );
10787
10788 err = GAITesterAddTestCase( inContext->tester, testCase );
10789 require_noerr( err, exit );
10790 testCase = NULL;
10791
10792 // Test Case #3:
10793 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10794
10795 _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
10796
10797 err = GAITestCaseCreate( title, &testCase );
10798 require_noerr( err, exit );
10799
10800 err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, kGAIPerfGAITimeLimitMs,
10801 inContext->defaultIterCount );
10802 require_noerr( err, exit );
10803
10804 err = GAITesterAddTestCase( inContext->tester, testCase );
10805 require_noerr( err, exit );
10806 testCase = NULL;
10807
10808 exit:
10809 if( testCase ) GAITestCaseFree( testCase );
10810 return( err );
10811 }
10812
10813 //===========================================================================================================================
10814 // GAIPerfTesterStopHandler
10815 //===========================================================================================================================
10816
10817 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10818 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10819 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10820
10821 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10822 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10823 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10824 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10825
10826 static void GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
10827 {
10828 OSStatus err;
10829 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10830 CFPropertyListRef plist;
10831 int exitCode;
10832
10833 err = inError;
10834 require_noerr_quiet( err, exit );
10835
10836 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
10837 "{"
10838 "%kO=" // info
10839 "{"
10840 "%kO=%lli" // callDelayMs
10841 "%kO=%lli" // serverDelayMs
10842 "%kO=%b" // skippedPathEval
10843 "%kO=%b" // usedBadUPDMode
10844 "}"
10845 "%kO=%O" // testCases
10846 "%kO=%b" // success
10847 "}",
10848 kGAIPerfResultsKey_Info,
10849 kGAIPerfInfoKey_CallDelay, (int64_t) context->callDelayMs,
10850 kGAIPerfInfoKey_ServerDelay, (int64_t) context->serverDelayMs,
10851 kGAIPerfInfoKey_SkippedPathEval, context->skipPathEval,
10852 kGAIPerfInfoKey_UsedBadUDPMode, context->badUDPMode,
10853 kGAIPerfResultsKey_TestCases, context->testCaseResults,
10854 kGAIPerfResultsKey_Success, !context->testFailed );
10855 require_noerr( err, exit );
10856
10857 err = OutputPropertyList( plist, context->outputFormat, context->appendNewline, context->outputFilePath );
10858 CFRelease( plist );
10859 require_noerr( err, exit );
10860
10861 exit:
10862 exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
10863 GAIPerfContextFree( context );
10864 exit( exitCode );
10865 }
10866
10867 //===========================================================================================================================
10868 // GAIPerfResultsHandler
10869 //===========================================================================================================================
10870
10871 // Keys for test case dictionary
10872
10873 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10874 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10875 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10876 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10877 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10878 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10879 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10880
10881 // Keys for test case results array entry dictionaries
10882
10883 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10884 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10885 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10886 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10887
10888 // Keys for test case stats dictionaries
10889
10890 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10891 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10892 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10893 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10894 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10895
10896 typedef struct
10897 {
10898 double min;
10899 double max;
10900 double mean;
10901 double stdDev;
10902
10903 } GAIPerfStats;
10904
10905 #define GAIPerfStatsInit( X ) \
10906 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10907
10908 static void
10909 GAIPerfResultsHandler(
10910 const char * inCaseTitle,
10911 NanoTime64 inCaseStartTime,
10912 NanoTime64 inCaseEndTime,
10913 const GAITestItemResult * inResultArray,
10914 size_t inResultCount,
10915 void * inContext )
10916 {
10917 OSStatus err;
10918 GAIPerfContext * const context = (GAIPerfContext *) inContext;
10919 int namesAreDynamic, namesAreUnique;
10920 const char * ptr;
10921 size_t count, startIndex;
10922 CFMutableArrayRef results = NULL;
10923 GAIPerfStats stats, firstStats, connStats;
10924 double sum, firstSum, connSum;
10925 size_t keyValueLen, i;
10926 char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10927 char startTimeStr[ 32 ];
10928 char endTimeStr[ 32 ];
10929 const GAITestItemResult * result;
10930
10931 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10932 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10933 // the domain name's CNAME, A, and AAAA records.
10934
10935 namesAreDynamic = false;
10936 namesAreUnique = false;
10937 ptr = inCaseTitle;
10938 while( ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
10939 {
10940 if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
10941 {
10942 namesAreDynamic = true;
10943 }
10944 else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
10945 {
10946 namesAreUnique = true;
10947 }
10948 if( namesAreDynamic && namesAreUnique ) break;
10949 }
10950
10951 startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
10952 results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
10953 require_action( results, exit, err = kNoMemoryErr );
10954
10955 GAIPerfStatsInit( &stats );
10956 GAIPerfStatsInit( &firstStats );
10957 GAIPerfStatsInit( &connStats );
10958
10959 sum = 0.0;
10960 firstSum = 0.0;
10961 connSum = 0.0;
10962 count = 0;
10963 for( i = startIndex; i < inResultCount; ++i )
10964 {
10965 double value;
10966
10967 result = &inResultArray[ i ];
10968
10969 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
10970 "{"
10971 "%kO=%s" // name
10972 "%kO=%lli" // connectionTimeUs
10973 "%kO=%lli" // firstTimeUs
10974 "%kO=%lli" // timeUs
10975 "%kO=%lli" // error
10976 "}",
10977 kGAIPerfTestCaseResultKey_Name, result->name,
10978 kGAIPerfTestCaseResultKey_ConnectionTime, (int64_t) result->connectionTimeUs,
10979 kGAIPerfTestCaseResultKey_FirstTime, (int64_t) result->firstTimeUs,
10980 kGAIPerfTestCaseResultKey_Time, (int64_t) result->timeUs,
10981 CFSTR( "error" ), (int64_t) result->error );
10982 require_noerr( err, exit );
10983
10984 if( !result->error )
10985 {
10986 value = (double) result->timeUs;
10987 if( value < stats.min ) stats.min = value;
10988 if( value > stats.max ) stats.max = value;
10989 sum += value;
10990
10991 value = (double) result->firstTimeUs;
10992 if( value < firstStats.min ) firstStats.min = value;
10993 if( value > firstStats.max ) firstStats.max = value;
10994 firstSum += value;
10995
10996 value = (double) result->connectionTimeUs;
10997 if( value < connStats.min ) connStats.min = value;
10998 if( value > connStats.max ) connStats.max = value;
10999 connSum += value;
11000
11001 ++count;
11002 }
11003 else
11004 {
11005 context->testFailed = true;
11006 }
11007 }
11008
11009 if( count > 0 )
11010 {
11011 stats.mean = sum / count;
11012 firstStats.mean = firstSum / count;
11013 connStats.mean = connSum / count;
11014
11015 sum = 0.0;
11016 firstSum = 0.0;
11017 connSum = 0.0;
11018 for( i = startIndex; i < inResultCount; ++i )
11019 {
11020 double diff;
11021
11022 result = &inResultArray[ i ];
11023 if( result->error ) continue;
11024
11025 diff = stats.mean - (double) result->timeUs;
11026 sum += ( diff * diff );
11027
11028 diff = firstStats.mean - (double) result->firstTimeUs;
11029 firstSum += ( diff * diff );
11030
11031 diff = connStats.mean - (double) result->connectionTimeUs;
11032 connSum += ( diff * diff );
11033 }
11034 stats.stdDev = sqrt( sum / count );
11035 firstStats.stdDev = sqrt( firstSum / count );
11036 connStats.stdDev = sqrt( connSum / count );
11037 }
11038
11039 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
11040 "{"
11041 "%kO=%s"
11042 "%kO=%s"
11043 "%kO=%s"
11044 "%kO=%O"
11045 "%kO="
11046 "{"
11047 "%kO=%lli"
11048 "%kO=%f"
11049 "%kO=%f"
11050 "%kO=%f"
11051 "%kO=%f"
11052 "}"
11053 "%kO="
11054 "{"
11055 "%kO=%lli"
11056 "%kO=%f"
11057 "%kO=%f"
11058 "%kO=%f"
11059 "%kO=%f"
11060 "}"
11061 "%kO="
11062 "{"
11063 "%kO=%lli"
11064 "%kO=%f"
11065 "%kO=%f"
11066 "%kO=%f"
11067 "%kO=%f"
11068 "}"
11069 "}",
11070 kGAIPerfTestCaseKey_Title, inCaseTitle,
11071 kGAIPerfTestCaseKey_StartTime, _NanoTime64ToDateString( inCaseStartTime, startTimeStr, sizeof( startTimeStr ) ),
11072 kGAIPerfTestCaseKey_EndTime, _NanoTime64ToDateString( inCaseEndTime, endTimeStr, sizeof( endTimeStr ) ),
11073 kGAIPerfTestCaseKey_Results, results,
11074 kGAIPerfTestCaseKey_Stats,
11075 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11076 kGAIPerfTestCaseStatsKey_Min, stats.min,
11077 kGAIPerfTestCaseStatsKey_Max, stats.max,
11078 kGAIPerfTestCaseStatsKey_Mean, stats.mean,
11079 kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev,
11080 kGAIPerfTestCaseKey_FirstStats,
11081 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11082 kGAIPerfTestCaseStatsKey_Min, firstStats.min,
11083 kGAIPerfTestCaseStatsKey_Max, firstStats.max,
11084 kGAIPerfTestCaseStatsKey_Mean, firstStats.mean,
11085 kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev,
11086 kGAIPerfTestCaseKey_ConnectionStats,
11087 kGAIPerfTestCaseStatsKey_Count, (int64_t) count,
11088 kGAIPerfTestCaseStatsKey_Min, connStats.min,
11089 kGAIPerfTestCaseStatsKey_Max, connStats.max,
11090 kGAIPerfTestCaseStatsKey_Mean, connStats.mean,
11091 kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev );
11092 require_noerr( err, exit );
11093
11094 exit:
11095 CFReleaseNullSafe( results );
11096 if( err ) exit( 1 );
11097 }
11098
11099 //===========================================================================================================================
11100 // GAIPerfSignalHandler
11101 //===========================================================================================================================
11102
11103 static void GAIPerfSignalHandler( void *inContext )
11104 {
11105 GAIPerfContext * const context = (GAIPerfContext *) inContext;
11106
11107 if( !context->tester ) exit( 1 );
11108 GAITesterStop( context->tester );
11109 context->tester = NULL;
11110 }
11111
11112 //===========================================================================================================================
11113 // GAITesterCreate
11114 //===========================================================================================================================
11115
11116 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11117 // possible strings to use in the Tag label.
11118
11119 #define kGAITesterTagStringLen 6
11120
11121 typedef struct GAITestItem GAITestItem;
11122 struct GAITestItem
11123 {
11124 GAITestItem * next; // Next test item in list.
11125 char * name; // Domain name to resolve.
11126 uint64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection.
11127 uint64_t firstTimeUs; // Time in microseconds that it took to get the first address result.
11128 uint64_t timeUs; // Time in microseconds that it took to get all expected address results.
11129 unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument.
11130 OSStatus error; // Current status/error.
11131 unsigned int timeLimitMs; // Time limit in milliseconds for the test item's completion.
11132 Boolean hasV4; // True if the domain name has one or more IPv4 addresses.
11133 Boolean hasV6; // True if the domain name has one or more IPv6 addresses.
11134 Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11135 Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11136 };
11137
11138 struct GAITestCase
11139 {
11140 GAITestCase * next; // Next test case in list.
11141 GAITestItem * itemList; // List of test items.
11142 char * title; // Title of the test case.
11143 };
11144
11145 struct GAITesterPrivate
11146 {
11147 CFRuntimeBase base; // CF object base.
11148 dispatch_queue_t queue; // Serial work queue.
11149 DNSServiceRef connection; // Reference to the shared DNS-SD connection.
11150 DNSServiceRef getAddrInfo; // Reference to the current DNSServiceGetAddrInfo operation.
11151 GAITestCase * caseList; // List of test cases.
11152 GAITestCase * currentCase; // Pointer to the current test case.
11153 GAITestItem * currentItem; // Pointer to the current test item.
11154 NanoTime64 caseStartTime; // Start time of current test case in Unix time as nanoseconds.
11155 NanoTime64 caseEndTime; // End time of current test case in Unix time as nanoseconds.
11156 int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11157 Boolean skipPathEval; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11158 Boolean stopped; // True if the tester has been stopped.
11159 Boolean badUDPMode; // True if the test DNS server is to run in Bad UDP mode.
11160 dispatch_source_t timer; // Timer for enforcing a test item's time limit.
11161 pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server.
11162 pid_t serverPID; // PID of the test DNS server.
11163 int serverDelayMs; // Additional time to have the server delay its responses by.
11164 int serverDefaultTTL; // Default TTL for the server's records.
11165 GAITesterStopHandler_f stopHandler; // User's stop handler.
11166 void * stopContext; // User's event handler context.
11167 GAITesterResultsHandler_f resultsHandler; // User's results handler.
11168 void * resultsContext; // User's results handler context.
11169
11170 // Variables for current test item.
11171
11172 uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received.
11173 uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received.
11174 uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo().
11175 uint64_t connTicks; // Ticks when the connection was created.
11176 uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received.
11177 uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received.
11178 Boolean gotFirstResult; // True if the first result has been received.
11179 };
11180
11181 CF_CLASS_DEFINE( GAITester );
11182
11183 static void _GAITesterStartNextTest( GAITesterRef inTester );
11184 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap );
11185 static void _GAITesterFirstGAITimeout( void *inContext );
11186 static void _GAITesterTimeout( void *inContext );
11187 static void DNSSD_API
11188 _GAITesterFirstGAICallback(
11189 DNSServiceRef inSDRef,
11190 DNSServiceFlags inFlags,
11191 uint32_t inInterfaceIndex,
11192 DNSServiceErrorType inError,
11193 const char * inHostname,
11194 const struct sockaddr * inSockAddr,
11195 uint32_t inTTL,
11196 void * inContext );
11197 static void DNSSD_API
11198 _GAITesterGetAddrInfoCallback(
11199 DNSServiceRef inSDRef,
11200 DNSServiceFlags inFlags,
11201 uint32_t inInterfaceIndex,
11202 DNSServiceErrorType inError,
11203 const char * inHostname,
11204 const struct sockaddr * inSockAddr,
11205 uint32_t inTTL,
11206 void * inContext );
11207 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
11208
11209 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11210
11211 static OSStatus
11212 GAITestItemCreate(
11213 const char * inName,
11214 unsigned int inAddressCount,
11215 GAITestAddrType inHasAddrs,
11216 GAITestAddrType inWantAddrs,
11217 unsigned int inTimeLimitMs,
11218 GAITestItem ** outItem );
11219 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
11220 static void GAITestItemFree( GAITestItem *inItem );
11221
11222 static OSStatus
11223 GAITesterCreate(
11224 dispatch_queue_t inQueue,
11225 int inCallDelayMs,
11226 int inServerDelayMs,
11227 int inServerDefaultTTL,
11228 Boolean inSkipPathEvaluation,
11229 Boolean inBadUDPMode,
11230 GAITesterRef * outTester )
11231 {
11232 OSStatus err;
11233 GAITesterRef obj = NULL;
11234
11235 CF_OBJECT_CREATE( GAITester, obj, err, exit );
11236
11237 ReplaceDispatchQueue( &obj->queue, inQueue );
11238 obj->callDelayMs = inCallDelayMs;
11239 obj->serverPID = -1;
11240 obj->serverDelayMs = inServerDelayMs;
11241 obj->serverDefaultTTL = inServerDefaultTTL;
11242 obj->skipPathEval = inSkipPathEvaluation;
11243 obj->badUDPMode = inBadUDPMode;
11244
11245 *outTester = obj;
11246 obj = NULL;
11247 err = kNoErr;
11248
11249 exit:
11250 CFReleaseNullSafe( obj );
11251 return( err );
11252 }
11253
11254 //===========================================================================================================================
11255 // _GAITesterFinalize
11256 //===========================================================================================================================
11257
11258 static void _GAITesterFinalize( CFTypeRef inObj )
11259 {
11260 GAITesterRef const me = (GAITesterRef) inObj;
11261 GAITestCase * testCase;
11262
11263 check( !me->getAddrInfo );
11264 check( !me->connection );
11265 check( !me->timer );
11266 dispatch_forget( &me->queue );
11267 while( ( testCase = me->caseList ) != NULL )
11268 {
11269 me->caseList = testCase->next;
11270 GAITestCaseFree( testCase );
11271 }
11272 }
11273
11274 //===========================================================================================================================
11275 // GAITesterStart
11276 //===========================================================================================================================
11277
11278 static void _GAITesterStart( void *inContext );
11279 static void _GAITesterStop( GAITesterRef me, OSStatus inError );
11280
11281 static void GAITesterStart( GAITesterRef me )
11282 {
11283 CFRetain( me );
11284 dispatch_async_f( me->queue, me, _GAITesterStart );
11285 }
11286
11287 #define kGAITesterFirstGAITimeoutSecs 4
11288
11289 static void _GAITesterStart( void *inContext )
11290 {
11291 OSStatus err;
11292 GAITesterRef const me = (GAITesterRef) inContext;
11293 DNSServiceFlags flags;
11294 char name[ 64 ];
11295 char tag[ kGAITesterTagStringLen + 1 ];
11296
11297 err = SpawnCommand( &me->serverPID, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11298 (int64_t) getpid(),
11299 me->serverDefaultTTL >= 0, " --defaultTTL ",
11300 me->serverDefaultTTL >= 0, me->serverDefaultTTL,
11301 me->serverDelayMs >= 0, " --responseDelay ",
11302 me->serverDelayMs >= 0, me->serverDelayMs,
11303 me->badUDPMode, " --badUDPMode" );
11304 require_noerr_quiet( err, exit );
11305
11306 SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test",
11307 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
11308
11309 flags = 0;
11310 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11311
11312 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
11313 _GAITesterFirstGAICallback, me );
11314 require_noerr( err, exit );
11315
11316 err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
11317 require_noerr( err, exit );
11318
11319 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
11320 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
11321 _GAITesterFirstGAITimeout, me, &me->timer );
11322 require_noerr( err, exit );
11323 dispatch_resume( me->timer );
11324
11325 exit:
11326 if( err ) _GAITesterStop( me, err );
11327 }
11328
11329 //===========================================================================================================================
11330 // GAITesterStop
11331 //===========================================================================================================================
11332
11333 static void _GAITesterUserStop( void *inContext );
11334
11335 static void GAITesterStop( GAITesterRef me )
11336 {
11337 CFRetain( me );
11338 dispatch_async_f( me->queue, me, _GAITesterUserStop );
11339 }
11340
11341 static void _GAITesterUserStop( void *inContext )
11342 {
11343 GAITesterRef const me = (GAITesterRef) inContext;
11344
11345 _GAITesterStop( me, kCanceledErr );
11346 CFRelease( me );
11347 }
11348
11349 static void _GAITesterStop( GAITesterRef me, OSStatus inError )
11350 {
11351 OSStatus err;
11352
11353 ForgetPacketCapture( &me->pcap );
11354 dispatch_source_forget( &me->timer );
11355 DNSServiceForget( &me->getAddrInfo );
11356 DNSServiceForget( &me->connection );
11357 if( me->serverPID != -1 )
11358 {
11359 err = kill( me->serverPID, SIGTERM );
11360 err = map_global_noerr_errno( err );
11361 check_noerr( err );
11362 me->serverPID = -1;
11363 }
11364
11365 if( !me->stopped )
11366 {
11367 me->stopped = true;
11368 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
11369 CFRelease( me );
11370 }
11371 }
11372
11373 //===========================================================================================================================
11374 // GAITesterAddTestCase
11375 //===========================================================================================================================
11376
11377 static OSStatus GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
11378 {
11379 OSStatus err;
11380 GAITestCase ** ptr;
11381
11382 require_action_quiet( inCase->itemList, exit, err = kCountErr );
11383
11384 for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
11385 *ptr = inCase;
11386 err = kNoErr;
11387
11388 exit:
11389 return( err );
11390 }
11391
11392 //===========================================================================================================================
11393 // GAITesterSetStopHandler
11394 //===========================================================================================================================
11395
11396 static void GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
11397 {
11398 me->stopHandler = inStopHandler;
11399 me->stopContext = inStopContext;
11400 }
11401
11402 //===========================================================================================================================
11403 // GAITesterSetResultsHandler
11404 //===========================================================================================================================
11405
11406 static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
11407 {
11408 me->resultsHandler = inResultsHandler;
11409 me->resultsContext = inResultsContext;
11410 }
11411
11412 //===========================================================================================================================
11413 // _GAITesterStartNextTest
11414 //===========================================================================================================================
11415
11416 static void _GAITesterStartNextTest( GAITesterRef me )
11417 {
11418 OSStatus err;
11419 GAITestItem * item;
11420 DNSServiceFlags flags;
11421 DNSServiceProtocol protocols;
11422 int done = false;
11423
11424 if( me->currentItem ) me->currentItem = me->currentItem->next;
11425
11426 if( !me->currentItem )
11427 {
11428 if( me->currentCase )
11429 {
11430 // No more test items means that the current test case has completed.
11431
11432 me->caseEndTime = NanoTimeGetCurrent();
11433
11434 if( me->resultsHandler )
11435 {
11436 size_t resultCount, i;
11437 GAITestItemResult * resultArray;
11438
11439 resultCount = 0;
11440 for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
11441 check( resultCount > 0 );
11442
11443 resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
11444 require_action( resultArray, exit, err = kNoMemoryErr );
11445
11446 item = me->currentCase->itemList;
11447 for( i = 0; i < resultCount; ++i )
11448 {
11449 resultArray[ i ].name = item->name;
11450 resultArray[ i ].connectionTimeUs = item->connectionTimeUs;
11451 resultArray[ i ].firstTimeUs = item->firstTimeUs;
11452 resultArray[ i ].timeUs = item->timeUs;
11453 resultArray[ i ].error = item->error;
11454 item = item->next;
11455 }
11456 me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
11457 me->resultsContext );
11458 ForgetMem( &resultArray );
11459 }
11460
11461 me->currentCase = me->currentCase->next;
11462 if( !me->currentCase )
11463 {
11464 done = true;
11465 err = kNoErr;
11466 goto exit;
11467 }
11468 }
11469 else
11470 {
11471 me->currentCase = me->caseList;
11472 }
11473 require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
11474 me->currentItem = me->currentCase->itemList;
11475 }
11476
11477 item = me->currentItem;
11478 check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
11479
11480 if( !item->wantV4 ) me->bitmapV4 = 0;
11481 else if( !item->hasV4 ) me->bitmapV4 = 1;
11482 else if( item->addressCount < 64 ) me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11483 else me->bitmapV4 = ~UINT64_C( 0 );
11484
11485 if( !item->wantV6 ) me->bitmapV6 = 0;
11486 else if( !item->hasV6 ) me->bitmapV6 = 1;
11487 else if( item->addressCount < 64 ) me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
11488 else me->bitmapV6 = ~UINT64_C( 0 );
11489 check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
11490 me->gotFirstResult = false;
11491
11492 // Perform preliminary tasks if this is the start of a new test case.
11493
11494 if( item == me->currentCase->itemList )
11495 {
11496 // Flush mDNSResponder's cache.
11497
11498 err = systemf( NULL, "killall -HUP mDNSResponder" );
11499 require_noerr( err, exit );
11500 sleep( 1 );
11501
11502 me->caseStartTime = NanoTimeGetCurrent();
11503 me->caseEndTime = kNanoTime_Invalid;
11504 }
11505
11506 // Start a packet capture.
11507
11508 check( !me->pcap );
11509 err = _GAITesterCreatePacketCapture( &me->pcap );
11510 require_noerr( err, exit );
11511
11512 // Start timer for test item's time limit.
11513
11514 check( !me->timer );
11515 if( item->timeLimitMs > 0 )
11516 {
11517 unsigned int timeLimitMs;
11518
11519 timeLimitMs = item->timeLimitMs;
11520 if( me->callDelayMs > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
11521 if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
11522
11523 err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
11524 ( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
11525 me->queue, _GAITesterTimeout, NULL, me, &me->timer );
11526 require_noerr( err, exit );
11527 dispatch_resume( me->timer );
11528 }
11529
11530 // Call DNSServiceGetAddrInfo().
11531
11532 if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
11533
11534 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
11535 if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
11536
11537 protocols = 0;
11538 if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
11539 if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
11540
11541 me->startTicks = UpTicks();
11542
11543 check( !me->connection );
11544 err = DNSServiceCreateConnection( &me->connection );
11545 require_noerr( err, exit );
11546
11547 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
11548 require_noerr( err, exit );
11549
11550 me->connTicks = UpTicks();
11551
11552 check( !me->getAddrInfo );
11553 me->getAddrInfo = me->connection;
11554 err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
11555 _GAITesterGetAddrInfoCallback, me );
11556 require_noerr( err, exit );
11557
11558 exit:
11559 if( err || done ) _GAITesterStop( me, err );
11560 }
11561
11562 //===========================================================================================================================
11563 // _GAITesterCreatePacketCapture
11564 //===========================================================================================================================
11565
11566 static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap )
11567 {
11568 OSStatus err;
11569 pcap_t * pcap;
11570 struct bpf_program program;
11571 char errBuf[ PCAP_ERRBUF_SIZE ];
11572
11573 pcap = pcap_create( "lo0", errBuf );
11574 require_action_string( pcap, exit, err = kUnknownErr, errBuf );
11575
11576 err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
11577 require_noerr_action( err, exit, err = kUnknownErr );
11578
11579 err = pcap_set_snaplen( pcap, 512 );
11580 require_noerr_action( err, exit, err = kUnknownErr );
11581
11582 err = pcap_set_immediate_mode( pcap, 0 );
11583 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11584
11585 err = pcap_activate( pcap );
11586 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11587
11588 err = pcap_setdirection( pcap, PCAP_D_INOUT );
11589 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11590
11591 err = pcap_setnonblock( pcap, 1, errBuf );
11592 require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
11593
11594 err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
11595 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11596
11597 err = pcap_setfilter( pcap, &program );
11598 pcap_freecode( &program );
11599 require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
11600
11601 *outPCap = pcap;
11602 pcap = NULL;
11603
11604 exit:
11605 if( pcap ) pcap_close( pcap );
11606 return( err );
11607 }
11608
11609 //===========================================================================================================================
11610 // _GAITesterFirstGAITimeout
11611 //===========================================================================================================================
11612
11613 static void _GAITesterFirstGAITimeout( void *inContext )
11614 {
11615 GAITesterRef const me = (GAITesterRef) inContext;
11616
11617 _GAITesterStop( me, kNoResourcesErr );
11618 }
11619
11620 //===========================================================================================================================
11621 // _GAITesterTimeout
11622 //===========================================================================================================================
11623
11624 static void _GAITesterTimeout( void *inContext )
11625 {
11626 GAITesterRef const me = (GAITesterRef) inContext;
11627
11628 _GAITesterCompleteCurrentTest( me, kTimeoutErr );
11629 }
11630
11631 //===========================================================================================================================
11632 // _GAITesterFirstGAICallback
11633 //===========================================================================================================================
11634
11635 static void DNSSD_API
11636 _GAITesterFirstGAICallback(
11637 DNSServiceRef inSDRef,
11638 DNSServiceFlags inFlags,
11639 uint32_t inInterfaceIndex,
11640 DNSServiceErrorType inError,
11641 const char * inHostname,
11642 const struct sockaddr * inSockAddr,
11643 uint32_t inTTL,
11644 void * inContext )
11645 {
11646 GAITesterRef const me = (GAITesterRef) inContext;
11647
11648 Unused( inSDRef );
11649 Unused( inInterfaceIndex );
11650 Unused( inHostname );
11651 Unused( inSockAddr );
11652 Unused( inTTL );
11653
11654 if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
11655 {
11656 dispatch_source_forget( &me->timer );
11657 DNSServiceForget( &me->getAddrInfo );
11658
11659 _GAITesterStartNextTest( me );
11660 }
11661 }
11662
11663 //===========================================================================================================================
11664 // _GAITesterGetAddrInfoCallback
11665 //===========================================================================================================================
11666
11667 static void DNSSD_API
11668 _GAITesterGetAddrInfoCallback(
11669 DNSServiceRef inSDRef,
11670 DNSServiceFlags inFlags,
11671 uint32_t inInterfaceIndex,
11672 DNSServiceErrorType inError,
11673 const char * inHostname,
11674 const struct sockaddr * inSockAddr,
11675 uint32_t inTTL,
11676 void * inContext )
11677 {
11678 OSStatus err;
11679 GAITesterRef const me = (GAITesterRef) inContext;
11680 GAITestItem * const item = me->currentItem;
11681 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
11682 uint64_t nowTicks;
11683 uint64_t * bitmapPtr;
11684 uint64_t bitmask;
11685 int hasAddr;
11686
11687 Unused( inSDRef );
11688 Unused( inInterfaceIndex );
11689 Unused( inHostname );
11690 Unused( inTTL );
11691
11692 nowTicks = UpTicks();
11693
11694 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
11695
11696 // Check if we were expecting an IP address result of this type.
11697
11698 if( sip->sa.sa_family == AF_INET )
11699 {
11700 bitmapPtr = &me->bitmapV4;
11701 hasAddr = item->hasV4;
11702 }
11703 else if( sip->sa.sa_family == AF_INET6 )
11704 {
11705 bitmapPtr = &me->bitmapV6;
11706 hasAddr = item->hasV6;
11707 }
11708 else
11709 {
11710 err = kTypeErr;
11711 goto exit;
11712 }
11713
11714 bitmask = 0;
11715 if( hasAddr )
11716 {
11717 uint32_t addrOffset;
11718
11719 require_noerr_action_quiet( inError, exit, err = inError );
11720
11721 if( sip->sa.sa_family == AF_INET )
11722 {
11723 const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr );
11724
11725 if( strcasecmp( item->name, "localhost." ) == 0 )
11726 {
11727 if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
11728 }
11729 else
11730 {
11731 addrOffset = addrV4 - kDNSServerBaseAddrV4;
11732 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11733 {
11734 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11735 }
11736 }
11737 }
11738 else
11739 {
11740 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
11741
11742 if( strcasecmp( item->name, "localhost." ) == 0 )
11743 {
11744 if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
11745 }
11746 else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
11747 {
11748 addrOffset = addrV6[ 15 ];
11749 if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
11750 {
11751 bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
11752 }
11753 }
11754 }
11755 }
11756 else
11757 {
11758 require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
11759 bitmask = 1;
11760 }
11761 require_action_quiet( bitmask != 0, exit, err = kValueErr );
11762 require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
11763
11764 *bitmapPtr &= ~bitmask;
11765 if( !me->gotFirstResult )
11766 {
11767 me->firstTicks = nowTicks;
11768 me->gotFirstResult = true;
11769 }
11770 err = kNoErr;
11771
11772 exit:
11773 if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
11774 {
11775 me->endTicks = nowTicks;
11776 _GAITesterCompleteCurrentTest( me, err );
11777 }
11778 }
11779
11780 //===========================================================================================================================
11781 // _GAITesterCompleteCurrentTest
11782 //===========================================================================================================================
11783
11784 static OSStatus
11785 _GAITesterGetDNSMessageFromPacket(
11786 const uint8_t * inPacketPtr,
11787 size_t inPacketLen,
11788 const uint8_t ** outMsgPtr,
11789 size_t * outMsgLen );
11790
11791 static void _GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
11792 {
11793 OSStatus err;
11794 GAITestItem * const item = me->currentItem;
11795 struct timeval timeStamps[ 4 ];
11796 struct timeval * tsPtr;
11797 struct timeval * tsQA = NULL;
11798 struct timeval * tsQAAAA = NULL;
11799 struct timeval * tsRA = NULL;
11800 struct timeval * tsRAAAA = NULL;
11801 struct timeval * t1;
11802 struct timeval * t2;
11803 int64_t idleTimeUs;
11804 uint8_t name[ kDomainNameLengthMax ];
11805
11806 dispatch_source_forget( &me->timer );
11807 DNSServiceForget( &me->getAddrInfo );
11808 DNSServiceForget( &me->connection );
11809
11810 item->error = inError;
11811 if( item->error )
11812 {
11813 err = kNoErr;
11814 goto exit;
11815 }
11816
11817 err = DomainNameFromString( name, item->name, NULL );
11818 require_noerr( err, exit );
11819
11820 tsPtr = &timeStamps[ 0 ];
11821 for( ;; )
11822 {
11823 int status;
11824 struct pcap_pkthdr * pktHdr;
11825 const uint8_t * packet;
11826 const uint8_t * msgPtr;
11827 size_t msgLen;
11828 const DNSHeader * hdr;
11829 unsigned int flags;
11830 const uint8_t * ptr;
11831 uint16_t qtype, qclass;
11832 uint8_t qname[ kDomainNameLengthMax ];
11833
11834 status = pcap_next_ex( me->pcap, &pktHdr, &packet );
11835 if( status != 1 ) break;
11836 if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
11837 if( msgLen < kDNSHeaderLength ) continue;
11838
11839 hdr = (const DNSHeader *) msgPtr;
11840 flags = DNSHeaderGetFlags( hdr );
11841 if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
11842 if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
11843
11844 ptr = (const uint8_t *) &hdr[ 1 ];
11845 if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
11846 if( qclass != kDNSServiceClass_IN ) continue;
11847 if( !DomainNameEqual( qname, name ) ) continue;
11848
11849 if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
11850 {
11851 if( flags & kDNSHeaderFlag_Response )
11852 {
11853 if( tsQA && !tsRA )
11854 {
11855 tsRA = tsPtr++;
11856 *tsRA = pktHdr->ts;
11857 }
11858 }
11859 else if( !tsQA )
11860 {
11861 tsQA = tsPtr++;
11862 *tsQA = pktHdr->ts;
11863 }
11864 }
11865 else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
11866 {
11867 if( flags & kDNSHeaderFlag_Response )
11868 {
11869 if( tsQAAAA && !tsRAAAA )
11870 {
11871 tsRAAAA = tsPtr++;
11872 *tsRAAAA = pktHdr->ts;
11873 }
11874 }
11875 else if( !tsQAAAA )
11876 {
11877 tsQAAAA = tsPtr++;
11878 *tsQAAAA = pktHdr->ts;
11879 }
11880 }
11881 }
11882
11883 // t1 is the time when the last query was sent.
11884
11885 if( tsQA && tsQAAAA ) t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
11886 else t1 = tsQA ? tsQA : tsQAAAA;
11887
11888 // t2 is when the first response was received.
11889
11890 if( tsRA && tsRAAAA ) t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
11891 else t2 = tsRA ? tsRA : tsRAAAA;
11892
11893 if( t1 && t2 )
11894 {
11895 idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
11896 if( idleTimeUs < 0 ) idleTimeUs = 0;
11897 }
11898 else
11899 {
11900 idleTimeUs = 0;
11901 }
11902
11903 item->connectionTimeUs = UpTicksToMicroseconds( me->connTicks - me->startTicks );
11904 item->firstTimeUs = UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11905 item->timeUs = UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs;
11906
11907 exit:
11908 ForgetPacketCapture( &me->pcap );
11909 if( err ) _GAITesterStop( me, err );
11910 else _GAITesterStartNextTest( me );
11911 }
11912
11913 //===========================================================================================================================
11914 // _GAITesterGetDNSMessageFromPacket
11915 //===========================================================================================================================
11916
11917 #define kHeaderSizeNullLink 4
11918 #define kHeaderSizeIPv4Min 20
11919 #define kHeaderSizeIPv6 40
11920 #define kHeaderSizeUDP 8
11921
11922 #define kIPProtocolUDP 0x11
11923
11924 static OSStatus
11925 _GAITesterGetDNSMessageFromPacket(
11926 const uint8_t * inPacketPtr,
11927 size_t inPacketLen,
11928 const uint8_t ** outMsgPtr,
11929 size_t * outMsgLen )
11930 {
11931 OSStatus err;
11932 const uint8_t * nullLink;
11933 uint32_t addressFamily;
11934 const uint8_t * ip;
11935 int ipHeaderLen;
11936 int protocol;
11937 const uint8_t * msg;
11938 const uint8_t * const end = &inPacketPtr[ inPacketLen ];
11939
11940 nullLink = &inPacketPtr[ 0 ];
11941 require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
11942 addressFamily = ReadHost32( &nullLink[ 0 ] );
11943
11944 ip = &nullLink[ kHeaderSizeNullLink ];
11945 if( addressFamily == AF_INET )
11946 {
11947 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
11948 ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4;
11949 protocol = ip[ 9 ];
11950 }
11951 else if( addressFamily == AF_INET6 )
11952 {
11953 require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
11954 ipHeaderLen = kHeaderSizeIPv6;
11955 protocol = ip[ 6 ];
11956 }
11957 else
11958 {
11959 err = kTypeErr;
11960 goto exit;
11961 }
11962 require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
11963 require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
11964
11965 msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
11966
11967 *outMsgPtr = msg;
11968 *outMsgLen = (size_t)( end - msg );
11969 err = kNoErr;
11970
11971 exit:
11972 return( err );
11973 }
11974
11975 //===========================================================================================================================
11976 // GAITestCaseCreate
11977 //===========================================================================================================================
11978
11979 static OSStatus GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
11980 {
11981 OSStatus err;
11982 GAITestCase * obj;
11983
11984 obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
11985 require_action( obj, exit, err = kNoMemoryErr );
11986
11987 obj->title = strdup( inTitle );
11988 require_action( obj->title, exit, err = kNoMemoryErr );
11989
11990 *outCase = obj;
11991 obj = NULL;
11992 err = kNoErr;
11993
11994 exit:
11995 if( obj ) GAITestCaseFree( obj );
11996 return( err );
11997 }
11998
11999 //===========================================================================================================================
12000 // GAITestCaseFree
12001 //===========================================================================================================================
12002
12003 static void GAITestCaseFree( GAITestCase *inCase )
12004 {
12005 GAITestItem * item;
12006
12007 while( ( item = inCase->itemList ) != NULL )
12008 {
12009 inCase->itemList = item->next;
12010 GAITestItemFree( item );
12011 }
12012 ForgetMem( &inCase->title );
12013 free( inCase );
12014 }
12015
12016 //===========================================================================================================================
12017 // GAITestCaseAddItem
12018 //===========================================================================================================================
12019
12020 static OSStatus
12021 GAITestCaseAddItem(
12022 GAITestCase * inCase,
12023 unsigned int inAliasCount,
12024 unsigned int inAddressCount,
12025 int inTTL,
12026 GAITestAddrType inHasAddrs,
12027 GAITestAddrType inWantAddrs,
12028 unsigned int inTimeLimitMs,
12029 unsigned int inItemCount )
12030 {
12031 OSStatus err;
12032 GAITestItem * item;
12033 GAITestItem * item2;
12034 GAITestItem * newItemList = NULL;
12035 GAITestItem ** itemPtr;
12036 char * ptr;
12037 char * end;
12038 unsigned int i;
12039 char name[ 64 ];
12040 char tag[ kGAITesterTagStringLen + 1 ];
12041
12042 require_action_quiet( inItemCount > 0, exit, err = kNoErr );
12043
12044 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12045
12046 require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
12047 require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
12048 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12049
12050 ptr = &name[ 0 ];
12051 end = &name[ countof( name ) ];
12052
12053 // Add Alias label.
12054
12055 if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
12056 else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
12057
12058 // Add Count label.
12059
12060 SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
12061
12062 // Add TTL label.
12063
12064 if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
12065
12066 // Add Tag label.
12067
12068 SNPrintF_Add( &ptr, end, "tag-%s.",
12069 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
12070
12071 // Add IPv4 or IPv6 label if necessary.
12072
12073 if( inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
12074 else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
12075
12076 // Finally, add the d.test. labels.
12077
12078 SNPrintF_Add( &ptr, end, "d.test." );
12079
12080 // Create item.
12081
12082 err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
12083 require_noerr( err, exit );
12084
12085 newItemList = item;
12086 itemPtr = &item->next;
12087
12088 // Create repeat items.
12089
12090 for( i = 1; i < inItemCount; ++i )
12091 {
12092 err = GAITestItemDup( item, &item2 );
12093 require_noerr( err, exit );
12094
12095 *itemPtr = item2;
12096 itemPtr = &item2->next;
12097 }
12098
12099 // Append to test case's item list.
12100
12101 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12102 *itemPtr = newItemList;
12103 newItemList = NULL;
12104
12105 exit:
12106 while( ( item = newItemList ) != NULL )
12107 {
12108 newItemList = item->next;
12109 GAITestItemFree( item );
12110 }
12111 return( err );
12112 }
12113
12114 //===========================================================================================================================
12115 // GAITestCaseAddLocalHostItem
12116 //===========================================================================================================================
12117
12118 static OSStatus
12119 GAITestCaseAddLocalHostItem(
12120 GAITestCase * inCase,
12121 GAITestAddrType inWantAddrs,
12122 unsigned int inTimeLimitMs,
12123 unsigned int inItemCount )
12124 {
12125 OSStatus err;
12126 GAITestItem * item;
12127 GAITestItem * item2;
12128 GAITestItem * newItemList = NULL;
12129 GAITestItem ** itemPtr;
12130 unsigned int i;
12131
12132 require_action_quiet( inItemCount > 1, exit, err = kNoErr );
12133
12134 err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
12135 require_noerr( err, exit );
12136
12137 newItemList = item;
12138 itemPtr = &item->next;
12139
12140 // Create repeat items.
12141
12142 for( i = 1; i < inItemCount; ++i )
12143 {
12144 err = GAITestItemDup( item, &item2 );
12145 require_noerr( err, exit );
12146
12147 *itemPtr = item2;
12148 itemPtr = &item2->next;
12149 }
12150
12151 for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
12152 *itemPtr = newItemList;
12153 newItemList = NULL;
12154
12155 exit:
12156 while( ( item = newItemList ) != NULL )
12157 {
12158 newItemList = item->next;
12159 GAITestItemFree( item );
12160 }
12161 return( err );
12162 }
12163
12164 //===========================================================================================================================
12165 // GAITestItemCreate
12166 //===========================================================================================================================
12167
12168 static OSStatus
12169 GAITestItemCreate(
12170 const char * inName,
12171 unsigned int inAddressCount,
12172 GAITestAddrType inHasAddrs,
12173 GAITestAddrType inWantAddrs,
12174 unsigned int inTimeLimitMs,
12175 GAITestItem ** outItem )
12176 {
12177 OSStatus err;
12178 GAITestItem * obj = NULL;
12179
12180 require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
12181 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
12182 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
12183
12184 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12185 require_action( obj, exit, err = kNoMemoryErr );
12186
12187 obj->name = strdup( inName );
12188 require_action( obj->name, exit, err = kNoMemoryErr );
12189
12190 obj->addressCount = inAddressCount;
12191 obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12192 obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12193 obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
12194 obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
12195 obj->error = kInProgressErr;
12196 obj->timeLimitMs = inTimeLimitMs;
12197
12198 *outItem = obj;
12199 obj = NULL;
12200 err = kNoErr;
12201
12202 exit:
12203 if( obj ) GAITestItemFree( obj );
12204 return( err );
12205 }
12206
12207 //===========================================================================================================================
12208 // GAITestItemDup
12209 //===========================================================================================================================
12210
12211 static OSStatus GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
12212 {
12213 OSStatus err;
12214 GAITestItem * obj;
12215
12216 obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
12217 require_action( obj, exit, err = kNoMemoryErr );
12218
12219 *obj = *inItem;
12220 obj->next = NULL;
12221 if( inItem->name )
12222 {
12223 obj->name = strdup( inItem->name );
12224 require_action( obj->name, exit, err = kNoMemoryErr );
12225 }
12226
12227 *outItem = obj;
12228 obj = NULL;
12229 err = kNoErr;
12230
12231 exit:
12232 if( obj ) GAITestItemFree( obj );
12233 return( err );
12234 }
12235
12236 //===========================================================================================================================
12237 // GAITestItemFree
12238 //===========================================================================================================================
12239
12240 static void GAITestItemFree( GAITestItem *inItem )
12241 {
12242 ForgetMem( &inItem->name );
12243 free( inItem );
12244 }
12245
12246 //===========================================================================================================================
12247 // MDNSDiscoveryTestCmd
12248 //===========================================================================================================================
12249
12250 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12251
12252 typedef struct
12253 {
12254 DNSServiceRef query; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12255 dispatch_source_t queryTimer; // Used to time out the "about" TXT record query.
12256 NanoTime64 startTime; // When the test started.
12257 NanoTime64 endTime; // When the test ended.
12258 pid_t replierPID; // PID of mDNS replier.
12259 uint32_t ifIndex; // Index of interface to run the replier on.
12260 unsigned int instanceCount; // Desired number of service instances.
12261 unsigned int txtSize; // Desired size of each service instance's TXT record data.
12262 unsigned int recordCountA; // Desired number of A records per replier hostname.
12263 unsigned int recordCountAAAA; // Desired number of AAAA records per replier hostname.
12264 unsigned int maxDropCount; // Replier's --maxDropCount option argument.
12265 double ucastDropRate; // Replier's probability of dropping a unicast response.
12266 double mcastDropRate; // Replier's probability of dropping a multicast query or response.
12267 Boolean noAdditionals; // True if the replier is to not include additional records in responses.
12268 Boolean useIPv4; // True if the replier is to use IPv4.
12269 Boolean useIPv6; // True if the replier is to use IPv6.
12270 Boolean flushedCache; // True if mDNSResponder's record cache was flushed before testing.
12271 char * replierCommand; // Command used to run the replier.
12272 char * serviceType; // Type of services to browse for.
12273 ServiceBrowserRef browser; // Service browser.
12274 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
12275 const char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
12276 OutputFormatType outputFormat; // Format of test results output.
12277 Boolean outputAppendNewline; // True if a newline character should be appended to JSON output.
12278 char hostname[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12279 char tag[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12280
12281 } MDNSDiscoveryTestContext;
12282
12283 static OSStatus GetAnyMDNSInterface( char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
12284 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
12285 static void DNSSD_API
12286 _MDNSDiscoveryTestAboutQueryCallback(
12287 DNSServiceRef inSDRef,
12288 DNSServiceFlags inFlags,
12289 uint32_t inInterfaceIndex,
12290 DNSServiceErrorType inError,
12291 const char * inFullName,
12292 uint16_t inType,
12293 uint16_t inClass,
12294 uint16_t inRDataLen,
12295 const void * inRDataPtr,
12296 uint32_t inTTL,
12297 void * inContext );
12298 static void
12299 _MDNSDiscoveryTestServiceBrowserCallback(
12300 ServiceBrowserResults * inResults,
12301 OSStatus inError,
12302 void * inContext );
12303 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
12304
12305 static void MDNSDiscoveryTestCmd( void )
12306 {
12307 OSStatus err;
12308 MDNSDiscoveryTestContext * context;
12309 char queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
12310
12311 context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
12312 require_action( context, exit, err = kNoMemoryErr );
12313
12314 err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
12315 require_noerr_quiet( err, exit );
12316
12317 err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
12318 require_noerr_quiet( err, exit );
12319
12320 err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
12321 require_noerr_quiet( err, exit );
12322
12323 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
12324 require_noerr_quiet( err, exit );
12325
12326 err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
12327 require_noerr_quiet( err, exit );
12328
12329 err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
12330 require_noerr_quiet( err, exit );
12331
12332 err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
12333 require_noerr_quiet( err, exit );
12334
12335 err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
12336 require_noerr_quiet( err, exit );
12337
12338 context->replierPID = -1;
12339 context->instanceCount = (unsigned int) gMDNSDiscoveryTest_InstanceCount;
12340 context->txtSize = (unsigned int) gMDNSDiscoveryTest_TXTSize;
12341 context->browseTimeSecs = (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
12342 context->recordCountA = (unsigned int) gMDNSDiscoveryTest_RecordCountA;
12343 context->recordCountAAAA = (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
12344 context->ucastDropRate = gMDNSDiscoveryTest_UnicastDropRate;
12345 context->mcastDropRate = gMDNSDiscoveryTest_MulticastDropRate;
12346 context->maxDropCount = (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
12347 context->outputFilePath = gMDNSDiscoveryTest_OutputFilePath;
12348 context->outputAppendNewline = gMDNSDiscoveryTest_OutputAppendNewline ? true : false;
12349 context->noAdditionals = gMDNSDiscoveryTest_NoAdditionals ? true : false;
12350 context->useIPv4 = ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
12351 context->useIPv6 = ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
12352
12353 if( gMDNSDiscoveryTest_Interface )
12354 {
12355 err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
12356 require_noerr_quiet( err, exit );
12357 }
12358 else
12359 {
12360 err = GetAnyMDNSInterface( NULL, &context->ifIndex );
12361 require_noerr_quiet( err, exit );
12362 }
12363
12364 context->outputFormat = (OutputFormatType) CLIArgToValue( "format", gMDNSDiscoveryTest_OutputFormat, &err,
12365 kOutputFormatStr_JSON, kOutputFormatType_JSON,
12366 kOutputFormatStr_XML, kOutputFormatType_XML,
12367 kOutputFormatStr_Binary, kOutputFormatType_Binary,
12368 NULL );
12369 require_noerr_quiet( err, exit );
12370
12371 if( gMDNSDiscoveryTest_FlushCache )
12372 {
12373 err = CheckRootUser();
12374 require_noerr_quiet( err, exit );
12375
12376 err = systemf( NULL, "killall -HUP mDNSResponder" );
12377 require_noerr( err, exit );
12378 sleep( 1 );
12379 context->flushedCache = true;
12380 }
12381
12382 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
12383 context->hostname );
12384 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
12385
12386 ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
12387 require_action( context->serviceType, exit, err = kUnknownErr );
12388
12389 err = ASPrintF( &context->replierCommand,
12390 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12391 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12392 (int64_t) getpid(),
12393 context->ifIndex,
12394 context->hostname,
12395 context->tag,
12396 context->instanceCount,
12397 context->recordCountA,
12398 context->recordCountAAAA,
12399 context->ucastDropRate,
12400 context->mcastDropRate,
12401 context->maxDropCount,
12402 context->noAdditionals, " --noAdditionals",
12403 context->useIPv4, " --ipv4",
12404 context->useIPv6, " --ipv6" );
12405 require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
12406
12407 err = SpawnCommand( &context->replierPID, "%s", context->replierCommand );
12408 require_noerr_quiet( err, exit );
12409
12410 // Query for the replier's about TXT record. A response means it's fully up and running.
12411
12412 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
12413 err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
12414 kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
12415 require_noerr( err, exit );
12416
12417 err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
12418 require_noerr( err, exit );
12419
12420 err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
12421 DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
12422 _MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
12423 require_noerr( err, exit );
12424 dispatch_resume( context->queryTimer );
12425
12426 context->startTime = NanoTimeGetCurrent();
12427 dispatch_main();
12428
12429 exit:
12430 exit( 1 );
12431 }
12432
12433 //===========================================================================================================================
12434 // _MDNSDiscoveryTestFirstQueryTimeout
12435 //===========================================================================================================================
12436
12437 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
12438 {
12439 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12440
12441 dispatch_source_forget( &context->queryTimer );
12442
12443 FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12444 exit( 1 );
12445 }
12446
12447 //===========================================================================================================================
12448 // _MDNSDiscoveryTestAboutQueryCallback
12449 //===========================================================================================================================
12450
12451 static void DNSSD_API
12452 _MDNSDiscoveryTestAboutQueryCallback(
12453 DNSServiceRef inSDRef,
12454 DNSServiceFlags inFlags,
12455 uint32_t inInterfaceIndex,
12456 DNSServiceErrorType inError,
12457 const char * inFullName,
12458 uint16_t inType,
12459 uint16_t inClass,
12460 uint16_t inRDataLen,
12461 const void * inRDataPtr,
12462 uint32_t inTTL,
12463 void * inContext )
12464 {
12465 OSStatus err;
12466 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12467
12468 Unused( inSDRef );
12469 Unused( inInterfaceIndex );
12470 Unused( inFullName );
12471 Unused( inType );
12472 Unused( inClass );
12473 Unused( inRDataLen );
12474 Unused( inRDataPtr );
12475 Unused( inTTL );
12476
12477 err = inError;
12478 require_noerr( err, exit );
12479 require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
12480
12481 DNSServiceForget( &context->query );
12482 dispatch_source_forget( &context->queryTimer );
12483
12484 err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
12485 require_noerr( err, exit );
12486
12487 err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
12488 require_noerr( err, exit );
12489
12490 ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
12491 ServiceBrowserStart( context->browser );
12492
12493 exit:
12494 if( err ) exit( 1 );
12495 }
12496
12497 //===========================================================================================================================
12498 // _MDNSDiscoveryTestServiceBrowserCallback
12499 //===========================================================================================================================
12500
12501 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12502 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12503 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12504 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12505 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12506 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12507 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12508 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12509 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12510 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12511 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12512
12513 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12514 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12515 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12516 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12517 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12518 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12519 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12520 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12521 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12522 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12523
12524 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12525 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12526
12527 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12528 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12529 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12530 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12531 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12532 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12533 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12534
12535 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
12536 {
12537 OSStatus err;
12538 MDNSDiscoveryTestContext * const context = (MDNSDiscoveryTestContext *) inContext;
12539 const SBRDomain * domain;
12540 const SBRServiceType * type;
12541 const SBRServiceInstance * instance;
12542 const SBRServiceInstance ** instanceArray = NULL;
12543 const SBRIPAddress * ipaddr;
12544 size_t hostnameLen;
12545 const char * ptr;
12546 const char * end;
12547 unsigned int i;
12548 uint32_t u32;
12549 CFMutableArrayRef unexpectedInstances;
12550 CFMutableArrayRef missingInstances;
12551 CFMutableArrayRef incorrectInstances;
12552 CFMutableDictionaryRef plist = NULL;
12553 CFMutableDictionaryRef badDict = NULL;
12554 CFMutableArrayRef unexpectedAddrs = NULL;
12555 CFMutableArrayRef missingAddrs = NULL;
12556 uint64_t maxResolveTimeUs;
12557 int success = false;
12558 char startTimeStr[ 32 ];
12559 char endTimeStr[ 32 ];
12560
12561 context->endTime = NanoTimeGetCurrent();
12562
12563 err = inError;
12564 require_noerr( err, exit );
12565
12566 _NanoTime64ToDateString( context->startTime, startTimeStr, sizeof( startTimeStr ) );
12567 _NanoTime64ToDateString( context->endTime, endTimeStr, sizeof( endTimeStr ) );
12568 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
12569 "{"
12570 "%kO="
12571 "{"
12572 "%kO=%s" // replierCommand
12573 "%kO=%lli" // txtSize
12574 "%kO=%lli" // instanceCount
12575 "%kO=%lli" // recordCountA
12576 "%kO=%lli" // recordCountAAAA
12577 "%kO=%s" // hostname
12578 "%kO=%b" // noAdditionals
12579 "%kO=%f" // ucastDropRate
12580 "%kO=%f" // mcastDropRate
12581 "%kO=%i" // maxDropCount
12582 "}"
12583 "%kO=%s" // startTime
12584 "%kO=%s" // endTime
12585 "%kO=%lli" // browseTimeSecs
12586 "%kO=%s" // serviceType
12587 "%kO=%b" // flushedCache
12588 "%kO=[%@]" // unexpectedInstances
12589 "%kO=[%@]" // missingInstances
12590 "%kO=[%@]" // incorrectInstances
12591 "}",
12592 kMDNSDiscoveryTestResultsKey_ReplierInfo,
12593 kMDNSDiscoveryTestReplierInfoKey_Command, context->replierCommand,
12594 kMDNSDiscoveryTestReplierInfoKey_InstanceCount, (int64_t) context->instanceCount,
12595 kMDNSDiscoveryTestReplierInfoKey_TXTSize, (int64_t) context->txtSize,
12596 kMDNSDiscoveryTestReplierInfoKey_RecordCountA, (int64_t) context->recordCountA,
12597 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA, (int64_t) context->recordCountAAAA,
12598 kMDNSDiscoveryTestReplierInfoKey_Hostname, context->hostname,
12599 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals, context->noAdditionals,
12600 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate, context->ucastDropRate,
12601 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate, context->mcastDropRate,
12602 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount, context->maxDropCount,
12603 kMDNSDiscoveryTestResultsKey_StartTime, startTimeStr,
12604 kMDNSDiscoveryTestResultsKey_EndTime, endTimeStr,
12605 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs, (int64_t) context->browseTimeSecs,
12606 kMDNSDiscoveryTestResultsKey_ServiceType, context->serviceType,
12607 kMDNSDiscoveryTestResultsKey_FlushedCache, context->flushedCache,
12608 kMDNSDiscoveryTestResultsKey_UnexpectedInstances, &unexpectedInstances,
12609 kMDNSDiscoveryTestResultsKey_MissingInstances, &missingInstances,
12610 kMDNSDiscoveryTestResultsKey_IncorrectInstances, &incorrectInstances );
12611 require_noerr( err, exit );
12612
12613 for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local.") != 0 ); domain = domain->next ) {}
12614 require_action( domain, exit, err = kInternalErr );
12615
12616 for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
12617 require_action( type, exit, err = kInternalErr );
12618
12619 instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
12620 require_action( instanceArray, exit, err = kNoMemoryErr );
12621
12622 hostnameLen = strlen( context->hostname );
12623 for( instance = type->instanceList; instance; instance = instance->next )
12624 {
12625 unsigned int instanceNumber = 0;
12626
12627 if( strcmp_prefix( instance->name, context->hostname ) == 0 )
12628 {
12629 ptr = &instance->name[ hostnameLen ];
12630 if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
12631 {
12632 ptr += 2;
12633 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12634 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12635 {
12636 if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
12637 {
12638 instanceNumber = u32;
12639 }
12640 }
12641 }
12642 else if( *ptr == '\0' )
12643 {
12644 instanceNumber = 1;
12645 }
12646 }
12647 if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
12648 {
12649 check( !instanceArray[ instanceNumber - 1 ] );
12650 instanceArray[ instanceNumber - 1 ] = instance;
12651 }
12652 else
12653 {
12654 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
12655 "{"
12656 "%kO=%s"
12657 "%kO=%lli"
12658 "}",
12659 kMDNSDiscoveryTestUnexpectedInstanceKey_Name, instance->name,
12660 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex, (int64_t) instance->ifIndex );
12661 require_noerr( err, exit );
12662 }
12663 }
12664
12665 maxResolveTimeUs = 0;
12666 for( i = 1; i <= context->instanceCount; ++i )
12667 {
12668 int isHostnameValid;
12669 int isTXTValid;
12670
12671 instance = instanceArray[ i - 1 ];
12672 if( !instance )
12673 {
12674 if( i == 1 )
12675 {
12676 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
12677 require_noerr( err, exit );
12678 }
12679 else
12680 {
12681 char * instanceName = NULL;
12682
12683 ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
12684 require_action( instanceName, exit, err = kUnknownErr );
12685
12686 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
12687 free( instanceName );
12688 require_noerr( err, exit );
12689 }
12690 continue;
12691 }
12692
12693 if( !instance->hostname )
12694 {
12695 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
12696 "{"
12697 "%kO=%s"
12698 "%kO=%b"
12699 "}",
12700 kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12701 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, false );
12702 require_noerr( err, exit );
12703 continue;
12704 }
12705
12706 badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
12707 require_action( badDict, exit, err = kNoMemoryErr );
12708
12709 isHostnameValid = false;
12710 if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
12711 {
12712 ptr = &instance->hostname[ hostnameLen ];
12713 if( i == 1 )
12714 {
12715 if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
12716 }
12717 else if( *ptr == '-' )
12718 {
12719 ++ptr;
12720 for( end = ptr; isdigit_safe( *end ); ++end ) {}
12721 if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
12722 {
12723 if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
12724 }
12725 }
12726 }
12727 if( !isHostnameValid )
12728 {
12729 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
12730 kSizeCString );
12731 require_noerr( err, exit );
12732 }
12733
12734 if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
12735 {
12736 err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
12737 require_noerr( err, exit );
12738 }
12739
12740 isTXTValid = false;
12741 if( instance->txtLen == context->txtSize )
12742 {
12743 uint8_t name[ kDomainNameLengthMax ];
12744
12745 err = DomainNameFromString( name, instance->name, NULL );
12746 require_noerr( err, exit );
12747
12748 err = DomainNameAppendString( name, type->name, NULL );
12749 require_noerr( err, exit );
12750
12751 err = DomainNameAppendString( name, "local", NULL );
12752 require_noerr( err, exit );
12753
12754 if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
12755 }
12756 if( !isTXTValid )
12757 {
12758 char * hexStr = NULL;
12759
12760 ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
12761 require_action( hexStr, exit, err = kUnknownErr );
12762
12763 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
12764 free( hexStr );
12765 require_noerr( err, exit );
12766 }
12767
12768 if( isHostnameValid )
12769 {
12770 uint64_t addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
12771 unsigned int j;
12772 uint8_t addrV4[ 4 ];
12773 uint8_t addrV6[ 16 ];
12774
12775 if( context->recordCountA < 64 ) addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
12776 else addrV4Bitmap = ~UINT64_C( 0 );
12777
12778 if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
12779 else addrV6Bitmap = ~UINT64_C( 0 );
12780
12781 addrV4[ 0 ] = 0;
12782 WriteBig16( &addrV4[ 1 ], i );
12783 addrV4[ 3 ] = 0;
12784
12785 memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
12786 WriteBig16( &addrV6[ 12 ], i );
12787
12788 unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12789 require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
12790
12791 resolveTimeUs = 0;
12792 for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
12793 {
12794 const uint8_t * addrPtr;
12795 unsigned int lsb;
12796 int isAddrValid = false;
12797
12798 if( ipaddr->sip.sa.sa_family == AF_INET )
12799 {
12800 addrPtr = (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
12801 lsb = addrPtr[ 3 ];
12802 if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
12803 {
12804 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12805 addrV4Bitmap &= ~bitmask;
12806 isAddrValid = true;
12807 }
12808 }
12809 else if( ipaddr->sip.sa.sa_family == AF_INET6 )
12810 {
12811 addrPtr = ipaddr->sip.v6.sin6_addr.s6_addr;
12812 lsb = addrPtr[ 15 ];
12813 if( ( memcmp( addrPtr, addrV6, 15 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
12814 {
12815 bitmask = UINT64_C( 1 ) << ( lsb - 1 );
12816 addrV6Bitmap &= ~bitmask;
12817 isAddrValid = true;
12818 }
12819 }
12820 if( isAddrValid )
12821 {
12822 if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
12823 }
12824 else
12825 {
12826 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
12827 require_noerr( err, exit );
12828 }
12829 }
12830
12831 resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
12832 if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
12833
12834 if( CFArrayGetCount( unexpectedAddrs ) > 0 )
12835 {
12836 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
12837 }
12838 ForgetCF( &unexpectedAddrs );
12839
12840 missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12841 require_action( missingAddrs, exit, err = kNoMemoryErr );
12842
12843 for( j = 1; addrV4Bitmap != 0; ++j )
12844 {
12845 bitmask = UINT64_C( 1 ) << ( j - 1 );
12846 if( addrV4Bitmap & bitmask )
12847 {
12848 addrV4Bitmap &= ~bitmask;
12849 addrV4[ 3 ] = (uint8_t) j;
12850 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
12851 require_noerr( err, exit );
12852 }
12853 }
12854 for( j = 1; addrV6Bitmap != 0; ++j )
12855 {
12856 bitmask = UINT64_C( 1 ) << ( j - 1 );
12857 if( addrV6Bitmap & bitmask )
12858 {
12859 addrV6Bitmap &= ~bitmask;
12860 addrV6[ 15 ] = (uint8_t) j;
12861 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.16a", addrV6 );
12862 require_noerr( err, exit );
12863 }
12864 }
12865
12866 if( CFArrayGetCount( missingAddrs ) > 0 )
12867 {
12868 CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
12869 }
12870 ForgetCF( &missingAddrs );
12871 }
12872
12873 if( CFDictionaryGetCount( badDict ) > 0 )
12874 {
12875 err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
12876 kSizeCString );
12877 require_noerr( err, exit );
12878
12879 CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
12880 CFArrayAppendValue( incorrectInstances, badDict );
12881 }
12882 ForgetCF( &badDict );
12883 }
12884
12885 if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
12886 ( CFArrayGetCount( missingInstances ) == 0 ) &&
12887 ( CFArrayGetCount( incorrectInstances ) == 0 ) )
12888 {
12889 err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
12890 require_noerr( err, exit );
12891 success = true;
12892 }
12893 else
12894 {
12895 success = false;
12896 }
12897 CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
12898
12899 err = OutputPropertyList( plist, context->outputFormat, context->outputAppendNewline, context->outputFilePath );
12900 require_noerr_quiet( err, exit );
12901
12902 exit:
12903 ForgetCF( &context->browser );
12904 if( context->replierPID != -1 )
12905 {
12906 kill( context->replierPID, SIGTERM );
12907 context->replierPID = -1;
12908 }
12909 FreeNullSafe( instanceArray );
12910 CFReleaseNullSafe( plist );
12911 CFReleaseNullSafe( badDict );
12912 CFReleaseNullSafe( unexpectedAddrs );
12913 CFReleaseNullSafe( missingAddrs );
12914 exit( err ? 1 : ( success ? 0 : 2 ) );
12915 }
12916
12917 //===========================================================================================================================
12918 // _MDNSDiscoveryTestTXTRecordIsValid
12919 //===========================================================================================================================
12920
12921 static Boolean _MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
12922 {
12923 uint32_t hash;
12924 int n;
12925 const uint8_t * ptr;
12926 size_t i, wholeCount, remCount;
12927 uint8_t txtStr[ 16 ];
12928
12929 if( inTXTLen == 0 ) return( false );
12930
12931 hash = FNV1( inRecordName, DomainNameLength( inRecordName ) );
12932
12933 txtStr[ 0 ] = 15;
12934 n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12935 check( n == 15 );
12936
12937 ptr = inTXTPtr;
12938 wholeCount = inTXTLen / 16;
12939 for( i = 0; i < wholeCount; ++i )
12940 {
12941 if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
12942 ptr += 16;
12943 }
12944
12945 remCount = inTXTLen % 16;
12946 if( remCount > 0 )
12947 {
12948 txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12949 if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
12950 ptr += remCount;
12951 }
12952 check( ptr == &inTXTPtr[ inTXTLen ] );
12953 return( true );
12954 }
12955
12956 //===========================================================================================================================
12957 // DotLocalTestCmd
12958 //===========================================================================================================================
12959
12960 #define kDotLocalTestPreparationTimeLimitSecs 5
12961 #define kDotLocalTestSubTestDurationSecs 5
12962
12963 // Constants for SRV record query subtest.
12964
12965 #define kDotLocalTestSRV_Priority 1
12966 #define kDotLocalTestSRV_Weight 0
12967 #define kDotLocalTestSRV_Port 80
12968 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12969 #define kDotLocalTestSRV_TargetStr "www.example.com."
12970 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12971
12972 typedef enum
12973 {
12974 kDotLocalTestState_Unset = 0,
12975 kDotLocalTestState_Preparing = 1,
12976 kDotLocalTestState_GAIMDNSOnly = 2,
12977 kDotLocalTestState_GAIDNSOnly = 3,
12978 kDotLocalTestState_GAIBoth = 4,
12979 kDotLocalTestState_GAINeither = 5,
12980 kDotLocalTestState_GAINoSuchRecord = 6,
12981 kDotLocalTestState_QuerySRV = 7,
12982 kDotLocalTestState_Done = 8
12983
12984 } DotLocalTestState;
12985
12986 typedef struct
12987 {
12988 const char * testDesc; // Description of the current subtest.
12989 char * queryName; // Query name for GetAddrInfo or QueryRecord operation.
12990 dispatch_source_t timer; // Timer used for limiting the time for each subtest.
12991 NanoTime64 startTime; // Timestamp of when the subtest started.
12992 NanoTime64 endTime; // Timestamp of when the subtest ended.
12993 CFMutableArrayRef correctResults; // Operation results that were expected.
12994 CFMutableArrayRef duplicateResults; // Operation results that were expected, but were already received.
12995 CFMutableArrayRef unexpectedResults; // Operation results that were unexpected.
12996 OSStatus error; // Subtest's error code.
12997 uint32_t addrDNSv4; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12998 uint32_t addrMDNSv4; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12999 uint8_t addrDNSv6[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
13000 uint8_t addrMDNSv6[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
13001 Boolean hasDNSv4; // True if queryName has a DNS IPv4 address.
13002 Boolean hasDNSv6; // True if queryName has a DNS IPv6 address.
13003 Boolean hasMDNSv4; // True if queryName has an MDNS IPv4 address.
13004 Boolean hasMDNSv6; // True if queryName has an MDNS IPv6 address.
13005 Boolean needDNSv4; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13006 Boolean needDNSv6; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13007 Boolean needMDNSv4; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13008 Boolean needMDNSv6; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13009 Boolean needSRV; // True if operation is expecting, but hasn't received an SRV result.
13010
13011 } DotLocalSubtest;
13012
13013 typedef struct
13014 {
13015 dispatch_source_t timer; // Timer used for limiting the time for each state/subtest.
13016 DotLocalSubtest * subtest; // Current subtest's state.
13017 DNSServiceRef connection; // Shared connection for DNS-SD operations.
13018 DNSServiceRef op; // Reference for the current DNS-SD operation.
13019 DNSServiceRef op2; // Reference for mdnsreplier probe query used during preparing state.
13020 DNSRecordRef localSOARef; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13021 char * replierCmd; // Command used to invoke the mdnsreplier.
13022 char * serverCmd; // Command used to invoke the test DNS server.
13023 CFMutableArrayRef reportsGAI; // Reports for subtests that use DNSServiceGetAddrInfo.
13024 CFMutableArrayRef reportsQuerySRV; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13025 NanoTime64 startTime; // Timestamp for when the test started.
13026 NanoTime64 endTime; // Timestamp for when the test ended.
13027 DotLocalTestState state; // The test's current state.
13028 pid_t replierPID; // PID of spawned mdnsreplier.
13029 pid_t serverPID; // PID of spawned test DNS server.
13030 uint32_t ifIndex; // Interface index used for mdnsreplier.
13031 char * outputFilePath; // File to write test results to. If NULL, then write to stdout.
13032 OutputFormatType outputFormat; // Format of test results output.
13033 Boolean appendNewline; // True if a newline character should be appended to JSON output.
13034 Boolean registeredSOA; // True if the dummy local. SOA record was successfully registered.
13035 Boolean serverIsReady; // True if response was received for test DNS server probe query.
13036 Boolean replierIsReady; // True if response was received for mdnsreplier probe query.
13037 Boolean testFailed; // True if at least one subtest failed.
13038 char labelStr[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13039 // The format of this string is "dotlocal-test-<six random chars>".
13040 } DotLocalTestContext;
13041
13042 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext );
13043 static void DNSSD_API
13044 _DotLocalTestProbeQueryRecordCallback(
13045 DNSServiceRef inSDRef,
13046 DNSServiceFlags inFlags,
13047 uint32_t inInterfaceIndex,
13048 DNSServiceErrorType inError,
13049 const char * inFullName,
13050 uint16_t inType,
13051 uint16_t inClass,
13052 uint16_t inRDataLen,
13053 const void * inRDataPtr,
13054 uint32_t inTTL,
13055 void * inContext );
13056 static void DNSSD_API
13057 _DotLocalTestRegisterRecordCallback(
13058 DNSServiceRef inSDRef,
13059 DNSRecordRef inRecordRef,
13060 DNSServiceFlags inFlags,
13061 DNSServiceErrorType inError,
13062 void * inContext );
13063 static void _DotLocalTestTimerHandler( void *inContext );
13064 static void DNSSD_API
13065 _DotLocalTestGAICallback(
13066 DNSServiceRef inSDRef,
13067 DNSServiceFlags inFlags,
13068 uint32_t inInterfaceIndex,
13069 DNSServiceErrorType inError,
13070 const char * inHostname,
13071 const struct sockaddr * inSockAddr,
13072 uint32_t inTTL,
13073 void * inContext );
13074 static void DNSSD_API
13075 _DotLocalTestQueryRecordCallback(
13076 DNSServiceRef inSDRef,
13077 DNSServiceFlags inFlags,
13078 uint32_t inInterfaceIndex,
13079 DNSServiceErrorType inError,
13080 const char * inFullName,
13081 uint16_t inType,
13082 uint16_t inClass,
13083 uint16_t inRDataLen,
13084 const void * inRDataPtr,
13085 uint32_t inTTL,
13086 void * inContext );
13087
13088 static void DotLocalTestCmd( void )
13089 {
13090 OSStatus err;
13091 DotLocalTestContext * context;
13092 uint8_t * rdataPtr;
13093 size_t rdataLen;
13094 DNSServiceFlags flags;
13095 char queryName[ 64 ];
13096 char randBuf[ 6 + 1 ]; // Large enough for four and six character random strings below.
13097
13098 context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
13099 require_action( context, exit, err = kNoMemoryErr );
13100
13101 context->startTime = NanoTimeGetCurrent();
13102 context->endTime = kNanoTime_Invalid;
13103
13104 context->state = kDotLocalTestState_Preparing;
13105
13106 if( gDotLocalTest_Interface )
13107 {
13108 err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
13109 require_noerr_quiet( err, exit );
13110 }
13111 else
13112 {
13113 err = GetAnyMDNSInterface( NULL, &context->ifIndex );
13114 require_noerr_quiet( err, exit );
13115 }
13116
13117 if( gDotLocalTest_OutputFilePath )
13118 {
13119 context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
13120 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
13121 }
13122
13123 context->outputFormat = (OutputFormatType) CLIArgToValue( "format", gDotLocalTest_OutputFormat, &err,
13124 kOutputFormatStr_JSON, kOutputFormatType_JSON,
13125 kOutputFormatStr_XML, kOutputFormatType_XML,
13126 kOutputFormatStr_Binary, kOutputFormatType_Binary,
13127 NULL );
13128 require_noerr_quiet( err, exit );
13129
13130 context->appendNewline = gDotLocalTest_OutputAppendNewline ? true : false;
13131
13132 context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13133 require_action( context->reportsGAI, exit, err = kNoMemoryErr );
13134
13135 context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13136 require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
13137
13138 SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
13139 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
13140
13141 // Spawn an mdnsreplier.
13142
13143 err = ASPrintF( &context->replierCmd,
13144 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13145 " --countAAAA 1",
13146 (int64_t) getpid(), context->ifIndex, context->labelStr,
13147 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
13148 require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
13149
13150 err = SpawnCommand( &context->replierPID, "%s", context->replierCmd );
13151 require_noerr( err, exit );
13152
13153 // Spawn a test DNS server
13154
13155 err = ASPrintF( &context->serverCmd,
13156 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13157 (int64_t) getpid(), context->labelStr );
13158 require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
13159
13160 err = SpawnCommand( &context->serverPID, "%s", context->serverCmd );
13161 require_noerr( err, exit );
13162
13163 // Create a shared DNS-SD connection.
13164
13165 err = DNSServiceCreateConnection( &context->connection );
13166 require_noerr( err, exit );
13167
13168 err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
13169 require_noerr( err, exit );
13170
13171 // Create probe query for DNS server, i.e., query for any name that has an A record.
13172
13173 SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
13174
13175 flags = kDNSServiceFlagsShareConnection;
13176 #if( TARGET_OS_WATCH )
13177 flags |= kDNSServiceFlagsPathEvaluationDone;
13178 #endif
13179
13180 context->op = context->connection;
13181 err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
13182 kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
13183 require_noerr( err, exit );
13184
13185 // Create probe query for mdnsreplier's "about" TXT record.
13186
13187 SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
13188
13189 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
13190 #if( TARGET_OS_WATCH )
13191 flags |= kDNSServiceFlagsPathEvaluationDone;
13192 #endif
13193
13194 context->op2 = context->connection;
13195 err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
13196 _DotLocalTestProbeQueryRecordCallback, context );
13197 require_noerr( err, exit );
13198
13199 // Register a dummy local. SOA record.
13200
13201 err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
13202 1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
13203 require_noerr( err, exit );
13204
13205 err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
13206 kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN, 1,
13207 rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
13208 require_noerr( err, exit );
13209
13210 // Start timer for probe responses and SOA record registration.
13211
13212 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
13213 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13214 _DotLocalTestTimerHandler, context, &context->timer );
13215 require_noerr( err, exit );
13216 dispatch_resume( context->timer );
13217
13218 dispatch_main();
13219
13220 exit:
13221 if( err ) ErrQuit( 1, "error: %#m\n", err );
13222 }
13223
13224 //===========================================================================================================================
13225 // _DotLocalTestStateMachine
13226 //===========================================================================================================================
13227
13228 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
13229 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest );
13230 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext );
13231 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
13232 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
13233
13234 static void _DotLocalTestStateMachine( DotLocalTestContext *inContext )
13235 {
13236 OSStatus err;
13237 DotLocalTestState nextState;
13238
13239 DNSServiceForget( &inContext->op );
13240 DNSServiceForget( &inContext->op2 );
13241 dispatch_source_forget( &inContext->timer );
13242
13243 switch( inContext->state )
13244 {
13245 case kDotLocalTestState_Preparing: nextState = kDotLocalTestState_GAIMDNSOnly; break;
13246 case kDotLocalTestState_GAIMDNSOnly: nextState = kDotLocalTestState_GAIDNSOnly; break;
13247 case kDotLocalTestState_GAIDNSOnly: nextState = kDotLocalTestState_GAIBoth; break;
13248 case kDotLocalTestState_GAIBoth: nextState = kDotLocalTestState_GAINeither; break;
13249 case kDotLocalTestState_GAINeither: nextState = kDotLocalTestState_GAINoSuchRecord; break;
13250 case kDotLocalTestState_GAINoSuchRecord: nextState = kDotLocalTestState_QuerySRV; break;
13251 case kDotLocalTestState_QuerySRV: nextState = kDotLocalTestState_Done; break;
13252 default: err = kStateErr; goto exit;
13253 }
13254
13255 if( inContext->state == kDotLocalTestState_Preparing )
13256 {
13257 if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
13258 {
13259 FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13260 YesNoStr( inContext->registeredSOA ),
13261 YesNoStr( inContext->serverIsReady ),
13262 YesNoStr( inContext->replierIsReady ) );
13263 err = kNotPreparedErr;
13264 goto exit;
13265 }
13266 }
13267 else
13268 {
13269 err = _DotLocalTestFinalizeSubtest( inContext );
13270 require_noerr( err, exit );
13271 }
13272
13273 inContext->state = nextState;
13274 if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
13275 err = _DotLocalTestStartSubtest( inContext );
13276
13277 exit:
13278 if( err ) ErrQuit( 1, "error: %#m\n", err );
13279 }
13280
13281 //===========================================================================================================================
13282 // _DotLocalSubtestCreate
13283 //===========================================================================================================================
13284
13285 static OSStatus _DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
13286 {
13287 OSStatus err;
13288 DotLocalSubtest * obj;
13289
13290 obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
13291 require_action( obj, exit, err = kNoMemoryErr );
13292
13293 obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13294 require_action( obj->correctResults, exit, err = kNoMemoryErr );
13295
13296 obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13297 require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
13298
13299 obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
13300 require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
13301
13302 *outSubtest = obj;
13303 obj = NULL;
13304 err = kNoErr;
13305
13306 exit:
13307 if( obj ) _DotLocalSubtestFree( obj );
13308 return( err );
13309 }
13310
13311 //===========================================================================================================================
13312 // _DotLocalSubtestFree
13313 //===========================================================================================================================
13314
13315 static void _DotLocalSubtestFree( DotLocalSubtest *inSubtest )
13316 {
13317 ForgetMem( &inSubtest->queryName );
13318 ForgetCF( &inSubtest->correctResults );
13319 ForgetCF( &inSubtest->duplicateResults );
13320 ForgetCF( &inSubtest->unexpectedResults );
13321 free( inSubtest );
13322 }
13323
13324 //===========================================================================================================================
13325 // _DotLocalTestStartSubtest
13326 //===========================================================================================================================
13327
13328 static OSStatus _DotLocalTestStartSubtest( DotLocalTestContext *inContext )
13329 {
13330 OSStatus err;
13331 DotLocalSubtest * subtest = NULL;
13332 DNSServiceRef op = NULL;
13333 DNSServiceFlags flags;
13334
13335 err = _DotLocalSubtestCreate( &subtest );
13336 require_noerr( err, exit );
13337
13338 if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
13339 {
13340 ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
13341 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13342
13343 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13344 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13345
13346 subtest->addrMDNSv4 = htonl( 0x00000201 ); // 0.0.2.1
13347 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::2:1
13348 subtest->addrMDNSv6[ 13 ] = 2;
13349 subtest->addrMDNSv6[ 15 ] = 1;
13350
13351 subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
13352 }
13353
13354 else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
13355 {
13356 ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
13357 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13358
13359 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13360 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13361
13362 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13363 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13364 subtest->addrDNSv6[ 15 ] = 1;
13365
13366 subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
13367 }
13368
13369 else if( inContext->state == kDotLocalTestState_GAIBoth )
13370 {
13371 ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
13372 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13373
13374 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13375 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13376 subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
13377 subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
13378
13379 subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 ); // 203.0.113.1
13380 memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 ); // 2001:db8:1::1
13381 subtest->addrDNSv6[ 15 ] = 1;
13382
13383 subtest->addrMDNSv4 = htonl( 0x00000101 ); // 0.0.1.1
13384 memcpy( subtest->addrMDNSv6, kMDNSReplierBaseAddrV6, 16 ); // 2001:db8:2::1:1
13385 subtest->addrMDNSv6[ 13 ] = 1;
13386 subtest->addrMDNSv6[ 15 ] = 1;
13387
13388 subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
13389 }
13390
13391 else if( inContext->state == kDotLocalTestState_GAINeither )
13392 {
13393 ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
13394 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13395
13396 subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
13397 }
13398
13399 else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
13400 {
13401 ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
13402 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13403
13404 subtest->hasDNSv4 = subtest->needDNSv4 = true;
13405 subtest->hasDNSv6 = subtest->needDNSv6 = true;
13406 subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
13407 }
13408
13409 else if( inContext->state == kDotLocalTestState_QuerySRV )
13410 {
13411 ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13412 kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
13413 inContext->labelStr );
13414 require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
13415
13416 subtest->needSRV = true;
13417 subtest->testDesc = kDotLocalTestSubtestDesc_QuerySRV;
13418 }
13419
13420 else
13421 {
13422 err = kStateErr;
13423 goto exit;
13424 }
13425
13426 // Start new operation.
13427
13428 flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
13429 #if( TARGET_OS_WATCH )
13430 flags |= kDNSServiceFlagsPathEvaluationDone;
13431 #endif
13432
13433 subtest->startTime = NanoTimeGetCurrent();
13434 subtest->endTime = kNanoTime_Invalid;
13435
13436 if( inContext->state == kDotLocalTestState_QuerySRV )
13437 {
13438 op = inContext->connection;
13439 err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
13440 kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
13441 require_noerr( err, exit );
13442 }
13443 else
13444 {
13445 op = inContext->connection;
13446 err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
13447 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
13448 require_noerr( err, exit );
13449 }
13450
13451 // Start timer.
13452
13453 check( !inContext->timer );
13454 err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubTestDurationSecs ),
13455 INT64_C_safe( kDotLocalTestSubTestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
13456 _DotLocalTestTimerHandler, inContext, &inContext->timer );
13457 require_noerr( err, exit );
13458 dispatch_resume( inContext->timer );
13459
13460 check( !inContext->op );
13461 inContext->op = op;
13462 op = NULL;
13463
13464 check( !inContext->subtest );
13465 inContext->subtest = subtest;
13466 subtest = NULL;
13467
13468 exit:
13469 if( subtest ) _DotLocalSubtestFree( subtest );
13470 if( op ) DNSServiceRefDeallocate( op );
13471 return( err );
13472 }
13473
13474 //===========================================================================================================================
13475 // _DotLocalTestFinalizeSubtest
13476 //===========================================================================================================================
13477
13478 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13479 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13480 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13481 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13482 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13483 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13484 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13485 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13486 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13487 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13488 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13489 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13490 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13491 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13492 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13493
13494 static OSStatus _DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
13495 {
13496 OSStatus err;
13497 DotLocalSubtest * subtest;
13498 CFMutableDictionaryRef reportDict;
13499 CFMutableDictionaryRef resultsDict;
13500 CFMutableArrayRef missingResults, reportArray;
13501 char startTimeStr[ 32 ];
13502 char endTimeStr[ 32 ];
13503
13504 subtest = inContext->subtest;
13505 inContext->subtest = NULL;
13506
13507 subtest->endTime = NanoTimeGetCurrent();
13508 _NanoTime64ToDateString( subtest->startTime, startTimeStr, sizeof( startTimeStr ) );
13509 _NanoTime64ToDateString( subtest->endTime, endTimeStr, sizeof( endTimeStr ) );
13510
13511 reportDict = NULL;
13512 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
13513 "{"
13514 "%kO=%s" // startTime
13515 "%kO=%s" // endTime
13516 "%kO=%s" // queryName
13517 "%kO=%s" // description
13518 "%kO={%@}" // results
13519 "}",
13520 kDotLocalTestReportKey_StartTime, startTimeStr,
13521 kDotLocalTestReportKey_EndTime, endTimeStr,
13522 kDotLocalTestReportKey_QueryName, subtest->queryName,
13523 kDotLocalTestReportKey_Description, subtest->testDesc,
13524 kDotLocalTestReportKey_Results, &resultsDict );
13525 require_noerr( err, exit );
13526
13527 missingResults = NULL;
13528 switch( inContext->state )
13529 {
13530 case kDotLocalTestState_GAIMDNSOnly:
13531 case kDotLocalTestState_GAIDNSOnly:
13532 case kDotLocalTestState_GAIBoth:
13533 case kDotLocalTestState_GAINeither:
13534 if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
13535 {
13536 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13537 "["
13538 "%.4a" // Expected DNS IPv4 address
13539 "%.16a" // Expected DNS IPv6 address
13540 "%.4a" // Expected MDNS IPv4 address
13541 "%.16a" // Expected MDNS IPv6 address
13542 "]",
13543 subtest->needDNSv4 ? &subtest->addrDNSv4 : NULL,
13544 subtest->needDNSv6 ? subtest->addrDNSv6 : NULL,
13545 subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
13546 subtest->needMDNSv6 ? subtest->addrMDNSv6 : NULL );
13547 require_noerr( err, exit );
13548 }
13549 break;
13550
13551 case kDotLocalTestState_QuerySRV:
13552 if( subtest->needSRV )
13553 {
13554 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13555 "["
13556 "%s" // Expected SRV record data as a string.
13557 "]",
13558 kDotLocalTestSRV_ResultStr );
13559 require_noerr( err, exit );
13560 }
13561 break;
13562
13563 case kDotLocalTestState_GAINoSuchRecord:
13564 if( subtest->needDNSv4 || subtest->needDNSv6 )
13565 {
13566 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
13567 "["
13568 "%s" // No Such Record (A)
13569 "%s" // No Such Record (AAAA)
13570 "]",
13571 subtest->needDNSv4 ? kNoSuchRecordAStr : NULL,
13572 subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
13573 require_noerr( err, exit );
13574 }
13575 break;
13576
13577 default:
13578 err = kStateErr;
13579 goto exit;
13580 }
13581
13582 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
13583
13584 if( missingResults )
13585 {
13586 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
13587 ForgetCF( &missingResults );
13588 if( !subtest->error ) subtest->error = kNotFoundErr;
13589 }
13590
13591 if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
13592 {
13593 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
13594 if( !subtest->error ) subtest->error = kUnexpectedErr;
13595 }
13596
13597 if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
13598 {
13599 CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
13600 if( !subtest->error ) subtest->error = kDuplicateErr;
13601 }
13602
13603 if( subtest->error ) inContext->testFailed = true;
13604 err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
13605 require_noerr( err, exit );
13606
13607 reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
13608 CFArrayAppendValue( reportArray, reportDict );
13609
13610 exit:
13611 _DotLocalSubtestFree( subtest );
13612 CFReleaseNullSafe( reportDict );
13613 return( err );
13614 }
13615
13616 //===========================================================================================================================
13617 // _DotLocalTestFinalizeAndExit
13618 //===========================================================================================================================
13619
13620 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
13621 {
13622 OSStatus err;
13623 CFPropertyListRef plist;
13624 char startTimeStr[ 32 ];
13625 char endTimeStr[ 32 ];
13626
13627 check( !inContext->subtest );
13628 inContext->endTime = NanoTimeGetCurrent();
13629
13630 if( inContext->replierPID != -1 )
13631 {
13632 kill( inContext->replierPID, SIGTERM );
13633 inContext->replierPID = -1;
13634 }
13635 if( inContext->serverPID != -1 )
13636 {
13637 kill( inContext->serverPID, SIGTERM );
13638 inContext->serverPID = -1;
13639 }
13640 err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
13641 require_noerr( err, exit );
13642
13643 _NanoTime64ToDateString( inContext->startTime, startTimeStr, sizeof( startTimeStr ) );
13644 _NanoTime64ToDateString( inContext->endTime, endTimeStr, sizeof( endTimeStr ) );
13645
13646 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13647 "{"
13648 "%kO=%s" // startTime
13649 "%kO=%s" // endTime
13650 "%kO=%O" // testsGAI
13651 "%kO=%O" // testsQuerySRV
13652 "%kO=%b" // success
13653 "%kO=%s" // replierCmd
13654 "%kO=%s" // serverCmd
13655 "}",
13656 kDotLocalTestReportKey_StartTime, startTimeStr,
13657 kDotLocalTestReportKey_EndTime, endTimeStr,
13658 kDotLocalTestReportKey_GetAddrInfoTests, inContext->reportsGAI,
13659 kDotLocalTestReportKey_QuerySRVTests, inContext->reportsQuerySRV,
13660 kDotLocalTestReportKey_Success, inContext->testFailed ? false : true,
13661 kDotLocalTestReportKey_MDNSReplierCmd, inContext->replierCmd,
13662 kDotLocalTestReportKey_DNSServerCmd, inContext->serverCmd );
13663 require_noerr( err, exit );
13664
13665 ForgetCF( &inContext->reportsGAI );
13666 ForgetCF( &inContext->reportsQuerySRV );
13667
13668 err = OutputPropertyList( plist, inContext->outputFormat, inContext->appendNewline, inContext->outputFilePath );
13669 CFRelease( plist );
13670 require_noerr( err, exit );
13671
13672 exit( inContext->testFailed ? 2 : 0 );
13673
13674 exit:
13675 ErrQuit( 1, "error: %#m\n", err );
13676 }
13677
13678 //===========================================================================================================================
13679 // _DotLocalTestProbeQueryRecordCallback
13680 //===========================================================================================================================
13681
13682 static void DNSSD_API
13683 _DotLocalTestProbeQueryRecordCallback(
13684 DNSServiceRef inSDRef,
13685 DNSServiceFlags inFlags,
13686 uint32_t inInterfaceIndex,
13687 DNSServiceErrorType inError,
13688 const char * inFullName,
13689 uint16_t inType,
13690 uint16_t inClass,
13691 uint16_t inRDataLen,
13692 const void * inRDataPtr,
13693 uint32_t inTTL,
13694 void * inContext )
13695 {
13696 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13697
13698 Unused( inInterfaceIndex );
13699 Unused( inFullName );
13700 Unused( inType );
13701 Unused( inClass );
13702 Unused( inRDataLen );
13703 Unused( inRDataPtr );
13704 Unused( inTTL );
13705
13706 check( context->state == kDotLocalTestState_Preparing );
13707
13708 require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
13709
13710 if( inSDRef == context->op )
13711 {
13712 DNSServiceForget( &context->op );
13713 context->serverIsReady = true;
13714 }
13715 else if( inSDRef == context->op2 )
13716 {
13717 DNSServiceForget( &context->op2 );
13718 context->replierIsReady = true;
13719 }
13720
13721 if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
13722 {
13723 _DotLocalTestStateMachine( context );
13724 }
13725
13726 exit:
13727 return;
13728 }
13729
13730 //===========================================================================================================================
13731 // _DotLocalTestRegisterRecordCallback
13732 //===========================================================================================================================
13733
13734 static void DNSSD_API
13735 _DotLocalTestRegisterRecordCallback(
13736 DNSServiceRef inSDRef,
13737 DNSRecordRef inRecordRef,
13738 DNSServiceFlags inFlags,
13739 DNSServiceErrorType inError,
13740 void * inContext )
13741 {
13742 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13743
13744 Unused( inSDRef );
13745 Unused( inRecordRef );
13746 Unused( inFlags );
13747
13748 if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
13749
13750 if( !context->registeredSOA )
13751 {
13752 context->registeredSOA = true;
13753 if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
13754 }
13755 }
13756
13757 //===========================================================================================================================
13758 // _DotLocalTestTimerHandler
13759 //===========================================================================================================================
13760
13761 static void _DotLocalTestTimerHandler( void *inContext )
13762 {
13763 _DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
13764 }
13765
13766 //===========================================================================================================================
13767 // _DotLocalTestGAICallback
13768 //===========================================================================================================================
13769
13770 static void DNSSD_API
13771 _DotLocalTestGAICallback(
13772 DNSServiceRef inSDRef,
13773 DNSServiceFlags inFlags,
13774 uint32_t inInterfaceIndex,
13775 DNSServiceErrorType inError,
13776 const char * inHostname,
13777 const struct sockaddr * inSockAddr,
13778 uint32_t inTTL,
13779 void * inContext )
13780 {
13781 OSStatus err;
13782 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13783 DotLocalSubtest * const subtest = context->subtest;
13784 const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr;
13785
13786 Unused( inSDRef );
13787 Unused( inInterfaceIndex );
13788 Unused( inHostname );
13789 Unused( inTTL );
13790
13791 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13792 require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
13793
13794 if( context->state == kDotLocalTestState_GAINoSuchRecord )
13795 {
13796 if( inError == kDNSServiceErr_NoSuchRecord )
13797 {
13798 CFMutableArrayRef array = NULL;
13799 const char * noSuchRecordStr;
13800
13801 if( sip->sa.sa_family == AF_INET )
13802 {
13803 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13804 subtest->needDNSv4 = false;
13805
13806 noSuchRecordStr = kNoSuchRecordAStr;
13807 }
13808 else
13809 {
13810 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13811 subtest->needDNSv6 = false;
13812
13813 noSuchRecordStr = kNoSuchRecordAAAAStr;
13814 }
13815 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
13816 require_noerr( err, fatal );
13817 }
13818 else if( !inError )
13819 {
13820 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
13821 require_noerr( err, fatal );
13822 }
13823 else
13824 {
13825 err = inError;
13826 goto exit;
13827 }
13828 }
13829 else
13830 {
13831 if( !inError )
13832 {
13833 CFMutableArrayRef array = NULL;
13834
13835 if( sip->sa.sa_family == AF_INET )
13836 {
13837 const uint32_t addrV4 = sip->v4.sin_addr.s_addr;
13838
13839 if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
13840 {
13841 array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13842 subtest->needDNSv4 = false;
13843 }
13844 else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
13845 {
13846 array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
13847 subtest->needMDNSv4 = false;
13848 }
13849 }
13850 else
13851 {
13852 const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr;
13853
13854 if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
13855 {
13856 array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13857 subtest->needDNSv6 = false;
13858 }
13859 else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
13860 {
13861 array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
13862 subtest->needMDNSv6 = false;
13863 }
13864 }
13865 if( !array ) array = subtest->unexpectedResults;
13866 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
13867 require_noerr( err, fatal );
13868 }
13869 else if( inError == kDNSServiceErr_NoSuchRecord )
13870 {
13871 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
13872 ( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
13873 require_noerr( err, fatal );
13874 }
13875 else
13876 {
13877 err = inError;
13878 goto exit;
13879 }
13880 }
13881
13882 exit:
13883 if( err )
13884 {
13885 subtest->error = err;
13886 _DotLocalTestStateMachine( context );
13887 }
13888 return;
13889
13890 fatal:
13891 ErrQuit( 1, "error: %#m\n", err );
13892 }
13893
13894 //===========================================================================================================================
13895 // _DotLocalTestQueryRecordCallback
13896 //===========================================================================================================================
13897
13898 static void DNSSD_API
13899 _DotLocalTestQueryRecordCallback(
13900 DNSServiceRef inSDRef,
13901 DNSServiceFlags inFlags,
13902 uint32_t inInterfaceIndex,
13903 DNSServiceErrorType inError,
13904 const char * inFullName,
13905 uint16_t inType,
13906 uint16_t inClass,
13907 uint16_t inRDataLen,
13908 const void * inRDataPtr,
13909 uint32_t inTTL,
13910 void * inContext )
13911 {
13912 OSStatus err;
13913 DotLocalTestContext * const context = (DotLocalTestContext *) inContext;
13914 DotLocalSubtest * const subtest = context->subtest;
13915 const SRVRecordDataFixedFields * fields;
13916 const uint8_t * target;
13917 const uint8_t * ptr;
13918 const uint8_t * end;
13919 char * rdataStr;
13920 unsigned int priority, weight, port;
13921 CFMutableArrayRef array;
13922
13923 Unused( inSDRef );
13924 Unused( inInterfaceIndex );
13925 Unused( inFullName );
13926 Unused( inTTL );
13927
13928 check( context->state == kDotLocalTestState_QuerySRV );
13929
13930 err = inError;
13931 require_noerr_quiet( err, exit );
13932 require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
13933 require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
13934 require_action_quiet( inRDataLen > sizeof( SRVRecordDataFixedFields ), exit, err = kSizeErr );
13935
13936 fields = (const SRVRecordDataFixedFields *) inRDataPtr;
13937 SRVRecordDataFixedFieldsGet( fields, &priority, &weight, &port );
13938 target = (const uint8_t *) &fields[ 1 ];
13939 end = ( (const uint8_t *) inRDataPtr ) + inRDataLen;
13940 for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
13941
13942 if( ( priority == kDotLocalTestSRV_Priority ) &&
13943 ( weight == kDotLocalTestSRV_Weight ) &&
13944 ( port == kDotLocalTestSRV_Port ) &&
13945 ( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
13946 {
13947 array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
13948 subtest->needSRV = false;
13949 }
13950 else
13951 {
13952 array = subtest->unexpectedResults;
13953 }
13954
13955 rdataStr = NULL;
13956 DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, NULL, 0, &rdataStr );
13957 if( !rdataStr )
13958 {
13959 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
13960 require_action( rdataStr, fatal, err = kNoMemoryErr );
13961 }
13962
13963 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
13964 free( rdataStr );
13965 require_noerr( err, fatal );
13966
13967 exit:
13968 if( err )
13969 {
13970 subtest->error = err;
13971 _DotLocalTestStateMachine( context );
13972 }
13973 return;
13974
13975 fatal:
13976 ErrQuit( 1, "error: %#m\n", err );
13977 }
13978
13979 //===========================================================================================================================
13980 // ProbeConflictTestCmd
13981 //===========================================================================================================================
13982
13983 #define kProbeConflictTestService_DefaultName "name"
13984 #define kProbeConflictTestService_Port 60000
13985
13986 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13987 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13988
13989 typedef struct
13990 {
13991 const char * description;
13992 const char * program;
13993 Boolean expectsRename;
13994
13995 } ProbeConflictTestCase;
13996
13997 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13998 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13999 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
14000
14001 static const ProbeConflictTestCase kProbeConflictTestCases[] =
14002 {
14003 // No conflicts
14004
14005 { "No probe conflicts.", kPCTProgPreWait "probes n-n-n;" "send;" kPCTProgPostWait, false },
14006
14007 // One multicast probe conflict
14008
14009 { "One multicast probe conflict (1).", kPCTProgPreWait "probes m;" "send;" kPCTProgPostWait, false },
14010 { "One multicast probe conflict (2).", kPCTProgPreWait "probes n-m;" "send;" kPCTProgPostWait, false },
14011 { "One multicast probe conflict (3).", kPCTProgPreWait "probes n-n-m;" "send;" kPCTProgPostWait, false },
14012
14013 // One unicast probe conflict
14014
14015 { "One unicast probe conflict (1).", kPCTProgPreWait "probes u;" "send;" kPCTProgPostWait, true },
14016 { "One unicast probe conflict (2).", kPCTProgPreWait "probes n-u;" "send;" kPCTProgPostWait, true },
14017 { "One unicast probe conflict (3).", kPCTProgPreWait "probes n-n-u;" "send;" kPCTProgPostWait, true },
14018
14019 // One multicast and one unicast probe conflict
14020
14021 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;" "send;" kPCTProgPostWait, true },
14022 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;" "send;" kPCTProgPostWait, true },
14023 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;" "send;" kPCTProgPostWait, true },
14024 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;" "send;" kPCTProgPostWait, true },
14025 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;" "send;" kPCTProgPostWait, true },
14026 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14027 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;" "send;" kPCTProgPostWait, true },
14028 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;" "send;" kPCTProgPostWait, true },
14029 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
14030
14031 // Two multicast probe conflicts
14032
14033 { "Two multicast probe conflicts (1).", kPCTProgPreWait "probes m-m;" "send;" kPCTProgPostWait, true },
14034 { "Two multicast probe conflicts (2).", kPCTProgPreWait "probes m-n-m;" "send;" kPCTProgPostWait, true },
14035 { "Two multicast probe conflicts (3).", kPCTProgPreWait "probes m-n-n-m;" "send;" kPCTProgPostWait, true },
14036 { "Two multicast probe conflicts (4).", kPCTProgPreWait "probes n-m-m;" "send;" kPCTProgPostWait, true },
14037 { "Two multicast probe conflicts (5).", kPCTProgPreWait "probes n-m-n-m-n;" "send;" kPCTProgPostWait, true },
14038 { "Two multicast probe conflicts (6).", kPCTProgPreWait "probes n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14039 { "Two multicast probe conflicts (7).", kPCTProgPreWait "probes n-n-m-m;" "send;" kPCTProgPostWait, true },
14040 { "Two multicast probe conflicts (8).", kPCTProgPreWait "probes n-n-m-n-m;" "send;" kPCTProgPostWait, true },
14041 { "Two multicast probe conflicts (9).", kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
14042 };
14043
14044 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14045
14046 typedef struct
14047 {
14048 DNSServiceRef registration; // Test service registration.
14049 NanoTime64 testStartTime; // Test's start time.
14050 NanoTime64 startTime; // Current test case's start time.
14051 MDNSColliderRef collider; // mDNS collider object.
14052 CFMutableArrayRef results; // Array of test case results.
14053 char * serviceName; // Test service's instance name as a string. (malloced)
14054 char * serviceType; // Test service's service type as a string. (malloced)
14055 uint8_t * recordName; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14056 unsigned int testCaseIndex; // Index of the current test case.
14057 uint32_t ifIndex; // Index of the interface that the collider is to operate on.
14058 char * outputFilePath; // File to write test results to. If NULL, then write to stdout. (malloced)
14059 OutputFormatType outputFormat; // Format of test report output.
14060 Boolean appendNewline; // True if a newline character should be appended to JSON output.
14061 Boolean registered; // True if the test service instance is currently registered.
14062 Boolean testFailed; // True if at least one test case failed.
14063
14064 } ProbeConflictTestContext;
14065
14066 static void DNSSD_API
14067 _ProbeConflictTestRegisterCallback(
14068 DNSServiceRef inSDRef,
14069 DNSServiceFlags inFlags,
14070 DNSServiceErrorType inError,
14071 const char * inName,
14072 const char * inType,
14073 const char * inDomain,
14074 void * inContext );
14075 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
14076 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
14077 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
14078 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
14079
14080 static void ProbeConflictTestCmd( void )
14081 {
14082 OSStatus err;
14083 ProbeConflictTestContext * context;
14084 const char * serviceName;
14085 char tag[ 6 + 1 ];
14086
14087 context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
14088 require_action( context, exit, err = kNoMemoryErr );
14089
14090 if( gProbeConflictTest_Interface )
14091 {
14092 err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
14093 require_noerr_quiet( err, exit );
14094 }
14095 else
14096 {
14097 err = GetAnyMDNSInterface( NULL, &context->ifIndex );
14098 require_noerr_quiet( err, exit );
14099 }
14100
14101 if( gProbeConflictTest_OutputFilePath )
14102 {
14103 context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
14104 require_action( context->outputFilePath, exit, err = kNoMemoryErr );
14105 }
14106
14107 context->appendNewline = gProbeConflictTest_OutputAppendNewline ? true : false;
14108 context->outputFormat = (OutputFormatType) CLIArgToValue( "format", gProbeConflictTest_OutputFormat, &err,
14109 kOutputFormatStr_JSON, kOutputFormatType_JSON,
14110 kOutputFormatStr_XML, kOutputFormatType_XML,
14111 kOutputFormatStr_Binary, kOutputFormatType_Binary,
14112 NULL );
14113 require_noerr_quiet( err, exit );
14114
14115 context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
14116 require_action( context->results, exit, err = kNoMemoryErr );
14117
14118 serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
14119
14120 ASPrintF( &context->serviceType, "_pctest-%s._udp",
14121 _RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14122 require_action( context->serviceType, exit, err = kNoMemoryErr );
14123
14124 context->testStartTime = NanoTimeGetCurrent();
14125 err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
14126 NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
14127 require_noerr( err, exit );
14128
14129 err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
14130 require_noerr( err, exit );
14131
14132 dispatch_main();
14133
14134 exit:
14135 exit( 1 );
14136 }
14137
14138 //===========================================================================================================================
14139 // _ProbeConflictTestRegisterCallback
14140 //===========================================================================================================================
14141
14142 static void DNSSD_API
14143 _ProbeConflictTestRegisterCallback(
14144 DNSServiceRef inSDRef,
14145 DNSServiceFlags inFlags,
14146 DNSServiceErrorType inError,
14147 const char * inName,
14148 const char * inType,
14149 const char * inDomain,
14150 void * inContext )
14151 {
14152 OSStatus err;
14153 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14154
14155 Unused( inSDRef );
14156 Unused( inType );
14157 Unused( inDomain );
14158
14159 err = inError;
14160 require_noerr( err, exit );
14161
14162 if( !context->registered )
14163 {
14164 if( inFlags & kDNSServiceFlagsAdd )
14165 {
14166 uint8_t * ptr;
14167 size_t recordNameLen;
14168 unsigned int len;
14169 uint8_t name[ kDomainNameLengthMax ];
14170
14171 context->registered = true;
14172
14173 FreeNullSafe( context->serviceName );
14174 context->serviceName = strdup( inName );
14175 require_action( context->serviceName, exit, err = kNoMemoryErr );
14176
14177 err = DomainNameFromString( name, context->serviceName, NULL );
14178 require_noerr( err, exit );
14179
14180 err = DomainNameAppendString( name, context->serviceType, NULL );
14181 require_noerr( err, exit );
14182
14183 err = DomainNameAppendString( name, "local", NULL );
14184 require_noerr( err, exit );
14185
14186 ForgetMem( &context->recordName );
14187 err = DomainNameDup( name, &context->recordName, &recordNameLen );
14188 require_noerr( err, exit );
14189 require_fatal( recordNameLen > 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14190
14191 // Make the first label all caps so that it's easier to spot in system logs.
14192
14193 ptr = context->recordName;
14194 for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
14195
14196 err = _ProbeConflictTestStartNextTest( context );
14197 require_noerr( err, exit );
14198 }
14199 }
14200 else
14201 {
14202 if( !( inFlags & kDNSServiceFlagsAdd ) )
14203 {
14204 context->registered = false;
14205 err = _ProbeConflictTestStopCurrentTest( context, true );
14206 require_noerr( err, exit );
14207 }
14208 }
14209 err = kNoErr;
14210
14211 exit:
14212 if( err ) exit( 1 );
14213 }
14214
14215 //===========================================================================================================================
14216 // _ProbeConflictTestColliderStopHandler
14217 //===========================================================================================================================
14218
14219 static void _ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
14220 {
14221 OSStatus err;
14222 ProbeConflictTestContext * const context = (ProbeConflictTestContext *) inContext;
14223
14224 err = inError;
14225 require_noerr_quiet( err, exit );
14226
14227 ForgetCF( &context->collider );
14228
14229 err = _ProbeConflictTestStopCurrentTest( context, false );
14230 require_noerr( err, exit );
14231
14232 err = _ProbeConflictTestStartNextTest( context );
14233 require_noerr( err, exit );
14234
14235 exit:
14236 if( err ) exit( 1 );
14237 }
14238
14239 //===========================================================================================================================
14240 // _ProbeConflictTestStartNextTest
14241 //===========================================================================================================================
14242
14243 static OSStatus _ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
14244 {
14245 OSStatus err;
14246 const ProbeConflictTestCase * testCase;
14247
14248 check( !inContext->collider );
14249
14250 if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
14251 {
14252 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14253 }
14254 else
14255 {
14256 _ProbeConflictTestFinalizeAndExit( inContext );
14257 }
14258
14259 err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
14260 require_noerr( err, exit );
14261
14262 err = MDNSColliderSetProgram( inContext->collider, testCase->program );
14263 require_noerr( err, exit );
14264
14265 err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
14266 kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
14267 require_noerr( err, exit );
14268
14269 MDNSColliderSetProtocols( inContext->collider, kMDNSColliderProtocol_IPv4 );
14270 MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
14271 MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
14272
14273 inContext->startTime = NanoTimeGetCurrent();
14274 err = MDNSColliderStart( inContext->collider );
14275 require_noerr( err, exit );
14276
14277 exit:
14278 return( err );
14279 }
14280
14281 //===========================================================================================================================
14282 // _ProbeConflictTestStopCurrentTest
14283 //===========================================================================================================================
14284
14285 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14286 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14287 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14288 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14289 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14290 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14291
14292 static OSStatus _ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
14293 {
14294 OSStatus err;
14295 const ProbeConflictTestCase * testCase;
14296 NanoTime64 endTime;
14297 Boolean passed;
14298 char startTimeStr[ 32 ];
14299 char endTimeStr[ 32 ];
14300
14301 endTime = NanoTimeGetCurrent();
14302
14303 if( inContext->collider )
14304 {
14305 MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
14306 MDNSColliderStop( inContext->collider );
14307 CFRelease( inContext->collider );
14308 inContext->collider = NULL;
14309 }
14310
14311 testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
14312 passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
14313 if( !passed ) inContext->testFailed = true;
14314
14315 _NanoTime64ToDateString( inContext->startTime, startTimeStr, sizeof( startTimeStr ) );
14316 _NanoTime64ToDateString( endTime, endTimeStr, sizeof( endTimeStr ) );
14317
14318 err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
14319 "{"
14320 "%kO=%s" // description
14321 "%kO=%b" // expectedRename
14322 "%kO=%s" // startTime
14323 "%kO=%s" // endTime
14324 "%kO=%s" // serviceName
14325 "%kO=%b" // passed
14326 "}",
14327 kProbeConflictTestCaseResultKey_Description, testCase->description,
14328 kProbeConflictTestCaseResultKey_ExpectedRename, testCase->expectsRename,
14329 kProbeConflictTestCaseResultKey_StartTime, startTimeStr,
14330 kProbeConflictTestCaseResultKey_EndTime, endTimeStr,
14331 kProbeConflictTestCaseResultKey_ServiceName, inContext->serviceName,
14332 kProbeConflictTestCaseResultKey_Passed, passed );
14333 require_noerr( err, exit );
14334
14335 ++inContext->testCaseIndex;
14336
14337 exit:
14338 return( err );
14339 }
14340
14341 //===========================================================================================================================
14342 // _ProbeConflictTestFinalizeAndExit
14343 //===========================================================================================================================
14344
14345 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14346 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14347 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14348 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14349 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14350
14351 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
14352 {
14353 OSStatus err;
14354 CFPropertyListRef plist;
14355 NanoTime64 endTime;
14356 char startTimeStr[ 32 ];
14357 char endTimeStr[ 32 ];
14358
14359 endTime = NanoTimeGetCurrent();
14360
14361 check( !inContext->collider );
14362
14363 _NanoTime64ToDateString( inContext->testStartTime, startTimeStr, sizeof( startTimeStr ) );
14364 _NanoTime64ToDateString( endTime, endTimeStr, sizeof( endTimeStr ) );
14365
14366 err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
14367 "{"
14368 "%kO=%s" // startTime
14369 "%kO=%s" // endTime
14370 "%kO=%s" // serviceType
14371 "%kO=%O" // results
14372 "%kO=%b" // passed
14373 "}",
14374 kProbeConflictTestReportKey_StartTime, startTimeStr,
14375 kProbeConflictTestReportKey_EndTime, endTimeStr,
14376 kProbeConflictTestReportKey_ServiceType, inContext->serviceType,
14377 kProbeConflictTestReportKey_Results, inContext->results,
14378 kProbeConflictTestReportKey_Passed, inContext->testFailed ? false : true );
14379 require_noerr( err, exit );
14380 ForgetCF( &inContext->results );
14381
14382 err = OutputPropertyList( plist, inContext->outputFormat, inContext->appendNewline, inContext->outputFilePath );
14383 CFRelease( plist );
14384 require_noerr( err, exit );
14385
14386 exit( inContext->testFailed ? 2 : 0 );
14387
14388 exit:
14389 ErrQuit( 1, "error: %#m\n", err );
14390 }
14391
14392 //===========================================================================================================================
14393 // SSDPDiscoverCmd
14394 //===========================================================================================================================
14395
14396 #define kSSDPPort 1900
14397
14398 typedef struct
14399 {
14400 HTTPHeader header; // HTTP header object for sending and receiving.
14401 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
14402 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
14403 int receiveSecs; // After send, the amount of time to spend receiving.
14404 uint32_t ifindex; // Index of the interface over which to send the query.
14405 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
14406 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
14407
14408 } SSDPDiscoverContext;
14409
14410 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
14411 static void SSDPDiscoverReadHandler( void *inContext );
14412 static int SocketToPortNumber( SocketRef inSock );
14413 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
14414
14415 static void SSDPDiscoverCmd( void )
14416 {
14417 OSStatus err;
14418 struct timeval now;
14419 SSDPDiscoverContext * context;
14420 dispatch_source_t signalSource = NULL;
14421 SocketRef sockV4 = kInvalidSocketRef;
14422 SocketRef sockV6 = kInvalidSocketRef;
14423 ssize_t n;
14424 int sendCount;
14425
14426 // Set up SIGINT handler.
14427
14428 signal( SIGINT, SIG_IGN );
14429 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
14430 require_noerr( err, exit );
14431 dispatch_resume( signalSource );
14432
14433 // Check command parameters.
14434
14435 if( gSSDPDiscover_ReceiveSecs < -1 )
14436 {
14437 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
14438 err = kParamErr;
14439 goto exit;
14440 }
14441
14442 // Create context.
14443
14444 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
14445 require_action( context, exit, err = kNoMemoryErr );
14446
14447 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
14448 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
14449 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
14450
14451 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
14452 require_noerr_quiet( err, exit );
14453
14454 // Set up IPv4 socket.
14455
14456 if( context->useIPv4 )
14457 {
14458 int port;
14459 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
14460 require_noerr( err, exit );
14461
14462 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
14463 require_noerr( err, exit );
14464
14465 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
14466 err = map_socket_noerr_errno( sockV4, err );
14467 require_noerr( err, exit );
14468 }
14469
14470 // Set up IPv6 socket.
14471
14472 if( context->useIPv6 )
14473 {
14474 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
14475 require_noerr( err, exit );
14476
14477 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
14478 require_noerr( err, exit );
14479
14480 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
14481 err = map_socket_noerr_errno( sockV6, err );
14482 require_noerr( err, exit );
14483 }
14484
14485 // Print prologue.
14486
14487 SSDPDiscoverPrintPrologue( context );
14488
14489 // Send mDNS query message.
14490
14491 sendCount = 0;
14492 if( IsValidSocket( sockV4 ) )
14493 {
14494 struct sockaddr_in mcastAddr4;
14495
14496 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
14497 SIN_LEN_SET( &mcastAddr4 );
14498 mcastAddr4.sin_family = AF_INET;
14499 mcastAddr4.sin_port = htons( kSSDPPort );
14500 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
14501
14502 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
14503 require_noerr( err, exit );
14504
14505 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
14506 (socklen_t) sizeof( mcastAddr4 ) );
14507 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
14508 if( err )
14509 {
14510 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
14511 ForgetSocket( &sockV4 );
14512 }
14513 else
14514 {
14515 if( gSSDPDiscover_Verbose )
14516 {
14517 gettimeofday( &now, NULL );
14518 FPrintF( stdout, "---\n" );
14519 FPrintF( stdout, "Send time: %{du:time}\n", &now );
14520 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
14521 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
14522 FPrintF( stdout, "Message size: %zu\n", context->header.len );
14523 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
14524 }
14525 ++sendCount;
14526 }
14527 }
14528
14529 if( IsValidSocket( sockV6 ) )
14530 {
14531 struct sockaddr_in6 mcastAddr6;
14532
14533 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
14534 SIN6_LEN_SET( &mcastAddr6 );
14535 mcastAddr6.sin6_family = AF_INET6;
14536 mcastAddr6.sin6_port = htons( kSSDPPort );
14537 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
14538 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
14539 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
14540
14541 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
14542 require_noerr( err, exit );
14543
14544 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
14545 (socklen_t) sizeof( mcastAddr6 ) );
14546 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
14547 if( err )
14548 {
14549 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
14550 ForgetSocket( &sockV6 );
14551 }
14552 else
14553 {
14554 if( gSSDPDiscover_Verbose )
14555 {
14556 gettimeofday( &now, NULL );
14557 FPrintF( stdout, "---\n" );
14558 FPrintF( stdout, "Send time: %{du:time}\n", &now );
14559 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
14560 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
14561 FPrintF( stdout, "Message size: %zu\n", context->header.len );
14562 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
14563 }
14564 ++sendCount;
14565 }
14566 }
14567 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
14568
14569 // If there's no wait period after the send, then exit.
14570
14571 if( context->receiveSecs == 0 ) goto exit;
14572
14573 // Create dispatch read sources for socket(s).
14574
14575 if( IsValidSocket( sockV4 ) )
14576 {
14577 SocketContext * sockCtx;
14578
14579 err = SocketContextCreate( sockV4, context, &sockCtx );
14580 require_noerr( err, exit );
14581 sockV4 = kInvalidSocketRef;
14582
14583 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
14584 &context->readSourceV4 );
14585 if( err ) ForgetSocketContext( &sockCtx );
14586 require_noerr( err, exit );
14587
14588 dispatch_resume( context->readSourceV4 );
14589 }
14590
14591 if( IsValidSocket( sockV6 ) )
14592 {
14593 SocketContext * sockCtx;
14594
14595 err = SocketContextCreate( sockV6, context, &sockCtx );
14596 require_noerr( err, exit );
14597 sockV6 = kInvalidSocketRef;
14598
14599 err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
14600 &context->readSourceV6 );
14601 if( err ) ForgetSocketContext( &sockCtx );
14602 require_noerr( err, exit );
14603
14604 dispatch_resume( context->readSourceV6 );
14605 }
14606
14607 if( context->receiveSecs > 0 )
14608 {
14609 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
14610 Exit );
14611 }
14612 dispatch_main();
14613
14614 exit:
14615 ForgetSocket( &sockV4 );
14616 ForgetSocket( &sockV6 );
14617 dispatch_source_forget( &signalSource );
14618 if( err ) exit( 1 );
14619 }
14620
14621 static int SocketToPortNumber( SocketRef inSock )
14622 {
14623 OSStatus err;
14624 sockaddr_ip sip;
14625 socklen_t len;
14626
14627 len = (socklen_t) sizeof( sip );
14628 err = getsockname( inSock, &sip.sa, &len );
14629 err = map_socket_noerr_errno( inSock, err );
14630 check_noerr( err );
14631 return( err ? -1 : SockAddrGetPort( &sip ) );
14632 }
14633
14634 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
14635 {
14636 OSStatus err;
14637
14638 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
14639 require_noerr( err, exit );
14640
14641 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
14642 require_noerr( err, exit );
14643
14644 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
14645 require_noerr( err, exit );
14646
14647 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
14648 require_noerr( err, exit );
14649
14650 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
14651 require_noerr( err, exit );
14652
14653 err = HTTPHeader_Commit( inHeader );
14654 require_noerr( err, exit );
14655
14656 exit:
14657 return( err );
14658 }
14659
14660 //===========================================================================================================================
14661 // SSDPDiscoverPrintPrologue
14662 //===========================================================================================================================
14663
14664 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
14665 {
14666 const int receiveSecs = inContext->receiveSecs;
14667 const char * ifName;
14668 char ifNameBuf[ IF_NAMESIZE + 1 ];
14669 NetTransportType ifType;
14670
14671 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
14672
14673 ifType = kNetTransportType_Undefined;
14674 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
14675
14676 FPrintF( stdout, "Interface: %s/%d/%s\n",
14677 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
14678 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
14679 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
14680 FPrintF( stdout, "Receive duration: " );
14681 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
14682 else FPrintF( stdout, "∞\n" );
14683 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
14684 }
14685
14686 //===========================================================================================================================
14687 // SSDPDiscoverReadHandler
14688 //===========================================================================================================================
14689
14690 static void SSDPDiscoverReadHandler( void *inContext )
14691 {
14692 OSStatus err;
14693 struct timeval now;
14694 SocketContext * const sockCtx = (SocketContext *) inContext;
14695 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext;
14696 HTTPHeader * const header = &context->header;
14697 sockaddr_ip fromAddr;
14698 size_t msgLen;
14699
14700 gettimeofday( &now, NULL );
14701
14702 err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
14703 NULL, NULL, NULL, NULL );
14704 require_noerr( err, exit );
14705
14706 FPrintF( stdout, "---\n" );
14707 FPrintF( stdout, "Receive time: %{du:time}\n", &now );
14708 FPrintF( stdout, "Source: %##a\n", &fromAddr );
14709 FPrintF( stdout, "Message size: %zu\n", msgLen );
14710 header->len = msgLen;
14711 if( HTTPHeader_Validate( header ) )
14712 {
14713 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
14714 if( header->extraDataLen > 0 )
14715 {
14716 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
14717 }
14718 }
14719 else
14720 {
14721 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
14722 goto exit;
14723 }
14724
14725 exit:
14726 if( err ) exit( 1 );
14727 }
14728
14729 //===========================================================================================================================
14730 // HTTPHeader_Validate
14731 //
14732 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
14733 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
14734 //
14735 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
14736 //===========================================================================================================================
14737
14738 Boolean HTTPHeader_Validate( HTTPHeader *inHeader )
14739 {
14740 const char * src;
14741 const char * end;
14742
14743 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
14744
14745 require( inHeader->len < sizeof( inHeader->buf ), exit );
14746 src = inHeader->buf;
14747 end = src + inHeader->len;
14748 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
14749 {
14750 src += 4;
14751 }
14752 else
14753 {
14754 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
14755 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
14756
14757 for( ;; )
14758 {
14759 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
14760 if( src >= end ) goto exit;
14761 ++src;
14762 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
14763 {
14764 src += 2;
14765 break;
14766 }
14767 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
14768 {
14769 src += 1;
14770 break;
14771 }
14772 }
14773 }
14774 inHeader->extraDataPtr = src;
14775 inHeader->extraDataLen = (size_t)( end - src );
14776 inHeader->len = (size_t)( src - inHeader->buf );
14777 return( true );
14778
14779 exit:
14780 return( false );
14781 }
14782
14783 #if( TARGET_OS_DARWIN )
14784 //===========================================================================================================================
14785 // ResQueryCmd
14786 //===========================================================================================================================
14787
14788 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
14789
14790 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
14791 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
14792 int,
14793 ( const char *dname, int class, int type, u_char *answer, int anslen ),
14794 ( dname, class, type, answer, anslen ) );
14795
14796 // res_query() from libinfo
14797
14798 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
14799 SOFT_LINK_FUNCTION_EX( info, res_query,
14800 int,
14801 ( const char *dname, int class, int type, u_char *answer, int anslen ),
14802 ( dname, class, type, answer, anslen ) );
14803
14804 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
14805
14806 static void ResQueryCmd( void )
14807 {
14808 OSStatus err;
14809 res_query_f res_query_ptr;
14810 int n;
14811 uint16_t type, class;
14812 uint8_t answer[ 1024 ];
14813
14814 // Get pointer to one of the res_query() functions.
14815
14816 if( gResQuery_UseLibInfo )
14817 {
14818 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
14819 {
14820 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
14821 err = kNotFoundErr;
14822 goto exit;
14823 }
14824 res_query_ptr = soft_res_query;
14825 }
14826 else
14827 {
14828 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
14829 {
14830 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
14831 err = kNotFoundErr;
14832 goto exit;
14833 }
14834 res_query_ptr = soft_res_9_query;
14835 }
14836
14837 // Get record type.
14838
14839 err = RecordTypeFromArgString( gResQuery_Type, &type );
14840 require_noerr( err, exit );
14841
14842 // Get record class.
14843
14844 if( gResQuery_Class )
14845 {
14846 err = RecordClassFromArgString( gResQuery_Class, &class );
14847 require_noerr( err, exit );
14848 }
14849 else
14850 {
14851 class = kDNSServiceClass_IN;
14852 }
14853
14854 // Print prologue.
14855
14856 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
14857 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
14858 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
14859 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
14860 FPrintF( stdout, "---\n" );
14861
14862 // Call res_query().
14863
14864 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
14865 if( n < 0 )
14866 {
14867 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
14868 err = kUnknownErr;
14869 goto exit;
14870 }
14871
14872 // Print result.
14873
14874 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
14875
14876 exit:
14877 if( err ) exit( 1 );
14878 }
14879
14880 //===========================================================================================================================
14881 // ResolvDNSQueryCmd
14882 //===========================================================================================================================
14883
14884 // 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
14885 // avoid including the header file.
14886
14887 typedef void * dns_handle_t;
14888
14889 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
14890 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
14891 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
14892 int32_t, (
14893 dns_handle_t dns,
14894 const char * name,
14895 uint32_t dnsclass,
14896 uint32_t dnstype,
14897 char * buf,
14898 uint32_t len,
14899 struct sockaddr * from,
14900 uint32_t * fromlen ),
14901 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
14902
14903 static void ResolvDNSQueryCmd( void )
14904 {
14905 OSStatus err;
14906 int n;
14907 dns_handle_t dns = NULL;
14908 uint16_t type, class;
14909 sockaddr_ip from;
14910 uint32_t fromLen;
14911 uint8_t answer[ 1024 ];
14912
14913 // Make sure that the required symbols are available.
14914
14915 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
14916 {
14917 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
14918 err = kNotFoundErr;
14919 goto exit;
14920 }
14921
14922 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
14923 {
14924 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
14925 err = kNotFoundErr;
14926 goto exit;
14927 }
14928
14929 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
14930 {
14931 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
14932 err = kNotFoundErr;
14933 goto exit;
14934 }
14935
14936 // Get record type.
14937
14938 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
14939 require_noerr( err, exit );
14940
14941 // Get record class.
14942
14943 if( gResolvDNSQuery_Class )
14944 {
14945 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
14946 require_noerr( err, exit );
14947 }
14948 else
14949 {
14950 class = kDNSServiceClass_IN;
14951 }
14952
14953 // Get dns handle.
14954
14955 dns = soft_dns_open( gResolvDNSQuery_Path );
14956 if( !dns )
14957 {
14958 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
14959 err = kUnknownErr;
14960 goto exit;
14961 }
14962
14963 // Print prologue.
14964
14965 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
14966 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
14967 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
14968 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
14969 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
14970 FPrintF( stdout, "---\n" );
14971
14972 // Call dns_query().
14973
14974 memset( &from, 0, sizeof( from ) );
14975 fromLen = (uint32_t) sizeof( from );
14976 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
14977 &fromLen );
14978 if( n < 0 )
14979 {
14980 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
14981 err = kUnknownErr;
14982 goto exit;
14983 }
14984
14985 // Print result.
14986
14987 FPrintF( stdout, "From: %##a\n", &from );
14988 FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n );
14989
14990 exit:
14991 if( dns ) soft_dns_free( dns );
14992 if( err ) exit( 1 );
14993 }
14994
14995 //===========================================================================================================================
14996 // CFHostCmd
14997 //===========================================================================================================================
14998
14999 static void
15000 _CFHostResolveCallback(
15001 CFHostRef inHost,
15002 CFHostInfoType inInfoType,
15003 const CFStreamError * inError,
15004 void * inInfo );
15005
15006 static void CFHostCmd( void )
15007 {
15008 OSStatus err;
15009 CFStringRef name;
15010 Boolean success;
15011 CFHostRef host = NULL;
15012 CFHostClientContext context;
15013 CFStreamError streamErr;
15014
15015 name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
15016 require_action( name, exit, err = kUnknownErr );
15017
15018 host = CFHostCreateWithName( kCFAllocatorDefault, name );
15019 ForgetCF( &name );
15020 require_action( host, exit, err = kUnknownErr );
15021
15022 memset( &context, 0, sizeof( context ) );
15023 success = CFHostSetClient( host, _CFHostResolveCallback, &context );
15024 require_action( success, exit, err = kUnknownErr );
15025
15026 CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
15027
15028 // Print prologue.
15029
15030 FPrintF( stdout, "Hostname: %s\n", gCFHost_Name );
15031 FPrintF( stdout, "Start time: %{du:time}\n", NULL );
15032 FPrintF( stdout, "---\n" );
15033
15034 success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
15035 require_action( success, exit, err = kUnknownErr );
15036 err = kNoErr;
15037
15038 CFRunLoopRun();
15039
15040 exit:
15041 CFReleaseNullSafe( host );
15042 if( err ) exit( 1 );
15043 }
15044
15045 static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
15046 {
15047 OSStatus err;
15048 struct timeval now;
15049
15050 gettimeofday( &now, NULL );
15051
15052 Unused( inInfoType );
15053 Unused( inInfo );
15054
15055 if( inError && ( inError->domain != 0 ) && ( inError->error ) )
15056 {
15057 err = inError->error;
15058 if( inError->domain == kCFStreamErrorDomainNetDB )
15059 {
15060 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
15061 }
15062 else
15063 {
15064 FPrintF( stderr, "Error %#m\n", err );
15065 }
15066 }
15067 else
15068 {
15069 CFArrayRef addresses;
15070 CFIndex count, i;
15071 CFDataRef addrData;
15072 const struct sockaddr * sockAddr;
15073 Boolean wasResolved = false;
15074
15075 addresses = CFHostGetAddressing( inHost, &wasResolved );
15076 check( wasResolved );
15077
15078 if( addresses )
15079 {
15080 count = CFArrayGetCount( addresses );
15081 for( i = 0; i < count; ++i )
15082 {
15083 addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
15084 require_noerr( err, exit );
15085
15086 sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
15087 FPrintF( stdout, "%##a\n", sockAddr );
15088 }
15089 }
15090 err = kNoErr;
15091 }
15092
15093 FPrintF( stdout, "---\n" );
15094 FPrintF( stdout, "End time: %{du:time}\n", &now );
15095
15096 if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
15097
15098 exit:
15099 exit( err ? 1 : 0 );
15100 }
15101
15102 //===========================================================================================================================
15103 // DNSConfigAddCmd
15104 //
15105 // Note: Based on ajn's supplemental test tool.
15106 //===========================================================================================================================
15107
15108 static void DNSConfigAddCmd( void )
15109 {
15110 OSStatus err;
15111 CFMutableDictionaryRef dict = NULL;
15112 CFMutableArrayRef array = NULL;
15113 size_t i;
15114 SCDynamicStoreRef store = NULL;
15115 CFStringRef key = NULL;
15116 Boolean success;
15117
15118 // Create dictionary.
15119
15120 dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
15121 require_action( dict, exit, err = kNoMemoryErr );
15122
15123 // Add DNS server IP addresses.
15124
15125 array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
15126 require_action( array, exit, err = kNoMemoryErr );
15127
15128 for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
15129 {
15130 CFStringRef addrStr;
15131
15132 addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
15133 require_action( addrStr, exit, err = kUnknownErr );
15134
15135 CFArrayAppendValue( array, addrStr );
15136 CFRelease( addrStr );
15137 }
15138
15139 CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
15140 ForgetCF( &array );
15141
15142 // Add domains, if any.
15143
15144 array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
15145 require_action( array, exit, err = kNoMemoryErr );
15146
15147 if( gDNSConfigAdd_DomainCount > 0 )
15148 {
15149 for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
15150 {
15151 CFStringRef domainStr;
15152
15153 domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
15154 require_action( domainStr, exit, err = kUnknownErr );
15155
15156 CFArrayAppendValue( array, domainStr );
15157 CFRelease( domainStr );
15158 }
15159 }
15160 else
15161 {
15162 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
15163
15164 CFArrayAppendValue( array, CFSTR( "" ) );
15165 }
15166
15167 CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
15168 ForgetCF( &array );
15169
15170 // Add interface, if any.
15171
15172 if( gDNSConfigAdd_Interface )
15173 {
15174 err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
15175 require_noerr( err, exit );
15176
15177 CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
15178 }
15179
15180 // Set dictionary in dynamic store.
15181
15182 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
15183 err = map_scerror( store );
15184 require_noerr( err, exit );
15185
15186 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
15187 require_action( key, exit, err = kUnknownErr );
15188
15189 success = SCDynamicStoreSetValue( store, key, dict );
15190 require_action( success, exit, err = kUnknownErr );
15191
15192 exit:
15193 CFReleaseNullSafe( dict );
15194 CFReleaseNullSafe( array );
15195 CFReleaseNullSafe( store );
15196 CFReleaseNullSafe( key );
15197 gExitCode = err ? 1 : 0;
15198 }
15199
15200 //===========================================================================================================================
15201 // DNSConfigRemoveCmd
15202 //===========================================================================================================================
15203
15204 static void DNSConfigRemoveCmd( void )
15205 {
15206 OSStatus err;
15207 SCDynamicStoreRef store = NULL;
15208 CFStringRef key = NULL;
15209 Boolean success;
15210
15211 store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
15212 err = map_scerror( store );
15213 require_noerr( err, exit );
15214
15215 key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
15216 require_action( key, exit, err = kUnknownErr );
15217
15218 success = SCDynamicStoreRemoveValue( store, key );
15219 require_action( success, exit, err = kUnknownErr );
15220
15221 exit:
15222 CFReleaseNullSafe( store );
15223 CFReleaseNullSafe( key );
15224 gExitCode = err ? 1 : 0;
15225 }
15226 #endif // TARGET_OS_DARWIN
15227
15228 //===========================================================================================================================
15229 // DaemonVersionCmd
15230 //===========================================================================================================================
15231
15232 static void DaemonVersionCmd( void )
15233 {
15234 OSStatus err;
15235 uint32_t size, version;
15236 char strBuf[ 16 ];
15237
15238 size = (uint32_t) sizeof( version );
15239 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
15240 require_noerr( err, exit );
15241
15242 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
15243
15244 exit:
15245 if( err ) exit( 1 );
15246 }
15247
15248 //===========================================================================================================================
15249 // Exit
15250 //===========================================================================================================================
15251
15252 static void Exit( void *inContext )
15253 {
15254 const char * const reason = (const char *) inContext;
15255
15256 FPrintF( stdout, "---\n" );
15257 FPrintF( stdout, "End time: %{du:time}\n", NULL );
15258 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
15259 exit( gExitCode );
15260 }
15261
15262 //===========================================================================================================================
15263 // PrintFTimestampHandler
15264 //===========================================================================================================================
15265
15266 static int
15267 PrintFTimestampHandler(
15268 PrintFContext * inContext,
15269 PrintFFormat * inFormat,
15270 PrintFVAList * inArgs,
15271 void * inUserContext )
15272 {
15273 struct timeval now;
15274 const struct timeval * tv;
15275 struct tm * localTime;
15276 size_t len;
15277 int n;
15278 char dateTimeStr[ 32 ];
15279
15280 Unused( inUserContext );
15281
15282 tv = va_arg( inArgs->args, const struct timeval * );
15283 require_action_quiet( !inFormat->suppress, exit, n = 0 );
15284
15285 if( !tv )
15286 {
15287 gettimeofday( &now, NULL );
15288 tv = &now;
15289 }
15290 localTime = localtime( &tv->tv_sec );
15291 len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
15292 if( len == 0 ) dateTimeStr[ 0 ] = '\0';
15293
15294 n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
15295
15296 exit:
15297 return( n );
15298 }
15299
15300 //===========================================================================================================================
15301 // PrintFDNSMessageHandler
15302 //===========================================================================================================================
15303
15304 static int
15305 PrintFDNSMessageHandler(
15306 PrintFContext * inContext,
15307 PrintFFormat * inFormat,
15308 PrintFVAList * inArgs,
15309 void * inUserContext )
15310 {
15311 OSStatus err;
15312 const void * msgPtr;
15313 size_t msgLen;
15314 char * text;
15315 int n;
15316 Boolean isMDNS;
15317 Boolean printRawRData;
15318
15319 Unused( inUserContext );
15320
15321 msgPtr = va_arg( inArgs->args, const void * );
15322 msgLen = va_arg( inArgs->args, size_t );
15323 require_action_quiet( !inFormat->suppress, exit, n = 0 );
15324
15325 isMDNS = ( inFormat->altForm > 0 ) ? true : false;
15326 if( inFormat->precision == 0 ) printRawRData = false;
15327 else if( inFormat->precision == 1 ) printRawRData = true;
15328 else
15329 {
15330 n = PrintFCore( inContext, "<< BAD %%{du:dnsmsg} PRECISION >>" );
15331 goto exit;
15332 }
15333
15334 err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text );
15335 if( !err )
15336 {
15337 n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString );
15338 free( text );
15339 }
15340 else
15341 {
15342 n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
15343 }
15344
15345 exit:
15346 return( n );
15347 }
15348
15349 //===========================================================================================================================
15350 // PrintFAddRmvFlagsHandler
15351 //===========================================================================================================================
15352
15353 static int
15354 PrintFAddRmvFlagsHandler(
15355 PrintFContext * inContext,
15356 PrintFFormat * inFormat,
15357 PrintFVAList * inArgs,
15358 void * inUserContext )
15359 {
15360 DNSServiceFlags flags;
15361 int n;
15362
15363 Unused( inUserContext );
15364
15365 flags = va_arg( inArgs->args, DNSServiceFlags );
15366 require_action_quiet( !inFormat->suppress, exit, n = 0 );
15367
15368 n = PrintFCore( inContext, "%08X %s%c%c", flags,
15369 ( flags & kDNSServiceFlagsAdd ) ? "Add" : "Rmv",
15370 ( flags & kDNSServiceFlagsMoreComing ) ? '+' : ' ',
15371 ( flags & kDNSServiceFlagsExpiredAnswer ) ? '!' : ' ' );
15372
15373 exit:
15374 return( n );
15375 }
15376
15377 //===========================================================================================================================
15378 // GetDNSSDFlagsFromOpts
15379 //===========================================================================================================================
15380
15381 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
15382 {
15383 DNSServiceFlags flags;
15384
15385 flags = (DNSServiceFlags) gDNSSDFlags;
15386 if( flags & kDNSServiceFlagsShareConnection )
15387 {
15388 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
15389 kDNSServiceFlagsShareConnection );
15390 }
15391
15392 if( gDNSSDFlag_AllowExpiredAnswers ) flags |= kDNSServiceFlagsAllowExpiredAnswers;
15393 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
15394 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
15395 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
15396 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
15397 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
15398 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
15399 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
15400 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
15401 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
15402 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
15403 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
15404 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
15405 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
15406 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
15407 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
15408
15409 return( flags );
15410 }
15411
15412 //===========================================================================================================================
15413 // CreateConnectionFromArgString
15414 //===========================================================================================================================
15415
15416 static OSStatus
15417 CreateConnectionFromArgString(
15418 const char * inString,
15419 dispatch_queue_t inQueue,
15420 DNSServiceRef * outSDRef,
15421 ConnectionDesc * outDesc )
15422 {
15423 OSStatus err;
15424 DNSServiceRef sdRef = NULL;
15425 ConnectionType type;
15426 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
15427 uint8_t uuid[ 16 ];
15428
15429 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
15430 {
15431 err = DNSServiceCreateConnection( &sdRef );
15432 require_noerr( err, exit );
15433 type = kConnectionType_Normal;
15434 }
15435 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
15436 {
15437 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
15438
15439 err = StringToInt32( pidStr, &pid );
15440 if( err )
15441 {
15442 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
15443 err = kParamErr;
15444 goto exit;
15445 }
15446
15447 memset( uuid, 0, sizeof( uuid ) );
15448 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
15449 if( err )
15450 {
15451 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
15452 goto exit;
15453 }
15454 type = kConnectionType_DelegatePID;
15455 }
15456 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
15457 {
15458 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
15459
15460 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
15461
15462 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
15463 if( err )
15464 {
15465 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
15466 err = kParamErr;
15467 goto exit;
15468 }
15469
15470 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
15471 if( err )
15472 {
15473 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
15474 goto exit;
15475 }
15476 type = kConnectionType_DelegateUUID;
15477 }
15478 else
15479 {
15480 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
15481 err = kParamErr;
15482 goto exit;
15483 }
15484
15485 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
15486 require_noerr( err, exit );
15487
15488 *outSDRef = sdRef;
15489 if( outDesc )
15490 {
15491 outDesc->type = type;
15492 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
15493 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
15494 }
15495 sdRef = NULL;
15496
15497 exit:
15498 if( sdRef ) DNSServiceRefDeallocate( sdRef );
15499 return( err );
15500 }
15501
15502 //===========================================================================================================================
15503 // InterfaceIndexFromArgString
15504 //===========================================================================================================================
15505
15506 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
15507 {
15508 OSStatus err;
15509 uint32_t ifIndex;
15510
15511 if( inString )
15512 {
15513 ifIndex = if_nametoindex( inString );
15514 if( ifIndex == 0 )
15515 {
15516 err = StringToUInt32( inString, &ifIndex );
15517 if( err )
15518 {
15519 FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
15520 err = kParamErr;
15521 goto exit;
15522 }
15523 }
15524 }
15525 else
15526 {
15527 ifIndex = 0;
15528 }
15529
15530 *outIndex = ifIndex;
15531 err = kNoErr;
15532
15533 exit:
15534 return( err );
15535 }
15536
15537 //===========================================================================================================================
15538 // RecordDataFromArgString
15539 //===========================================================================================================================
15540
15541 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
15542 {
15543 OSStatus err;
15544 uint8_t * dataPtr = NULL;
15545 size_t dataLen;
15546
15547 if( 0 ) {}
15548
15549 // Domain name
15550
15551 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
15552 {
15553 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
15554
15555 err = StringToDomainName( str, &dataPtr, &dataLen );
15556 require_noerr_quiet( err, exit );
15557 }
15558
15559 // File path
15560
15561 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
15562 {
15563 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
15564
15565 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
15566 require_noerr( err, exit );
15567 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
15568 }
15569
15570 // Hexadecimal string
15571
15572 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
15573 {
15574 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
15575
15576 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
15577 require_noerr( err, exit );
15578 require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
15579 }
15580
15581 // IPv4 address string
15582
15583 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
15584 {
15585 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
15586
15587 err = StringToARecordData( str, &dataPtr, &dataLen );
15588 require_noerr_quiet( err, exit );
15589 }
15590
15591 // IPv6 address string
15592
15593 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
15594 {
15595 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
15596
15597 err = StringToAAAARecordData( str, &dataPtr, &dataLen );
15598 require_noerr_quiet( err, exit );
15599 }
15600
15601 // SRV record
15602
15603 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
15604 {
15605 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
15606
15607 err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
15608 require_noerr( err, exit );
15609 }
15610
15611 // String with escaped hex and octal bytes
15612
15613 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
15614 {
15615 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
15616 const char * const end = str + strlen( str );
15617 size_t copiedLen;
15618 size_t totalLen;
15619 Boolean success;
15620
15621 if( str < end )
15622 {
15623 success = ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
15624 require_action( success, exit, err = kParamErr );
15625 require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
15626
15627 dataLen = totalLen;
15628 dataPtr = (uint8_t *) malloc( dataLen );
15629 require_action( dataPtr, exit, err = kNoMemoryErr );
15630
15631 success = ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
15632 require_action( success, exit, err = kParamErr );
15633 check( copiedLen == dataLen );
15634 }
15635 else
15636 {
15637 dataPtr = NULL;
15638 dataLen = 0;
15639 }
15640 }
15641
15642 // TXT record
15643
15644 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
15645 {
15646 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
15647
15648 err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
15649 require_noerr( err, exit );
15650 }
15651
15652 // Unrecognized format
15653
15654 else
15655 {
15656 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
15657 err = kParamErr;
15658 goto exit;
15659 }
15660
15661 err = kNoErr;
15662 *outDataLen = dataLen;
15663 *outDataPtr = dataPtr;
15664 dataPtr = NULL;
15665
15666 exit:
15667 FreeNullSafe( dataPtr );
15668 return( err );
15669 }
15670
15671 //===========================================================================================================================
15672 // RecordTypeFromArgString
15673 //===========================================================================================================================
15674
15675 typedef struct
15676 {
15677 uint16_t value; // Record type's numeric value.
15678 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
15679
15680 } RecordType;
15681
15682 static const RecordType kRecordTypes[] =
15683 {
15684 // Common types.
15685
15686 { kDNSServiceType_A, "A" },
15687 { kDNSServiceType_AAAA, "AAAA" },
15688 { kDNSServiceType_PTR, "PTR" },
15689 { kDNSServiceType_SRV, "SRV" },
15690 { kDNSServiceType_TXT, "TXT" },
15691 { kDNSServiceType_CNAME, "CNAME" },
15692 { kDNSServiceType_SOA, "SOA" },
15693 { kDNSServiceType_NSEC, "NSEC" },
15694 { kDNSServiceType_NS, "NS" },
15695 { kDNSServiceType_MX, "MX" },
15696 { kDNSServiceType_ANY, "ANY" },
15697 { kDNSServiceType_OPT, "OPT" },
15698
15699 // Less common types.
15700
15701 { kDNSServiceType_MD, "MD" },
15702 { kDNSServiceType_NS, "NS" },
15703 { kDNSServiceType_MD, "MD" },
15704 { kDNSServiceType_MF, "MF" },
15705 { kDNSServiceType_MB, "MB" },
15706 { kDNSServiceType_MG, "MG" },
15707 { kDNSServiceType_MR, "MR" },
15708 { kDNSServiceType_NULL, "NULL" },
15709 { kDNSServiceType_WKS, "WKS" },
15710 { kDNSServiceType_HINFO, "HINFO" },
15711 { kDNSServiceType_MINFO, "MINFO" },
15712 { kDNSServiceType_RP, "RP" },
15713 { kDNSServiceType_AFSDB, "AFSDB" },
15714 { kDNSServiceType_X25, "X25" },
15715 { kDNSServiceType_ISDN, "ISDN" },
15716 { kDNSServiceType_RT, "RT" },
15717 { kDNSServiceType_NSAP, "NSAP" },
15718 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
15719 { kDNSServiceType_SIG, "SIG" },
15720 { kDNSServiceType_KEY, "KEY" },
15721 { kDNSServiceType_PX, "PX" },
15722 { kDNSServiceType_GPOS, "GPOS" },
15723 { kDNSServiceType_LOC, "LOC" },
15724 { kDNSServiceType_NXT, "NXT" },
15725 { kDNSServiceType_EID, "EID" },
15726 { kDNSServiceType_NIMLOC, "NIMLOC" },
15727 { kDNSServiceType_ATMA, "ATMA" },
15728 { kDNSServiceType_NAPTR, "NAPTR" },
15729 { kDNSServiceType_KX, "KX" },
15730 { kDNSServiceType_CERT, "CERT" },
15731 { kDNSServiceType_A6, "A6" },
15732 { kDNSServiceType_DNAME, "DNAME" },
15733 { kDNSServiceType_SINK, "SINK" },
15734 { kDNSServiceType_APL, "APL" },
15735 { kDNSServiceType_DS, "DS" },
15736 { kDNSServiceType_SSHFP, "SSHFP" },
15737 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
15738 { kDNSServiceType_RRSIG, "RRSIG" },
15739 { kDNSServiceType_DNSKEY, "DNSKEY" },
15740 { kDNSServiceType_DHCID, "DHCID" },
15741 { kDNSServiceType_NSEC3, "NSEC3" },
15742 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
15743 { kDNSServiceType_HIP, "HIP" },
15744 { kDNSServiceType_SPF, "SPF" },
15745 { kDNSServiceType_UINFO, "UINFO" },
15746 { kDNSServiceType_UID, "UID" },
15747 { kDNSServiceType_GID, "GID" },
15748 { kDNSServiceType_UNSPEC, "UNSPEC" },
15749 { kDNSServiceType_TKEY, "TKEY" },
15750 { kDNSServiceType_TSIG, "TSIG" },
15751 { kDNSServiceType_IXFR, "IXFR" },
15752 { kDNSServiceType_AXFR, "AXFR" },
15753 { kDNSServiceType_MAILB, "MAILB" },
15754 { kDNSServiceType_MAILA, "MAILA" }
15755 };
15756
15757 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
15758 {
15759 OSStatus err;
15760 int32_t i32;
15761 const RecordType * type;
15762 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
15763
15764 for( type = kRecordTypes; type < end; ++type )
15765 {
15766 if( strcasecmp( type->name, inString ) == 0 )
15767 {
15768 *outValue = type->value;
15769 return( kNoErr );
15770 }
15771 }
15772
15773 err = StringToInt32( inString, &i32 );
15774 require_noerr_quiet( err, exit );
15775 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
15776
15777 *outValue = (uint16_t) i32;
15778
15779 exit:
15780 return( err );
15781 }
15782
15783 //===========================================================================================================================
15784 // RecordClassFromArgString
15785 //===========================================================================================================================
15786
15787 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
15788 {
15789 OSStatus err;
15790 int32_t i32;
15791
15792 if( strcasecmp( inString, "IN" ) == 0 )
15793 {
15794 *outValue = kDNSServiceClass_IN;
15795 err = kNoErr;
15796 goto exit;
15797 }
15798
15799 err = StringToInt32( inString, &i32 );
15800 require_noerr_quiet( err, exit );
15801 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
15802
15803 *outValue = (uint16_t) i32;
15804
15805 exit:
15806 return( err );
15807 }
15808
15809 //===========================================================================================================================
15810 // InterfaceIndexToName
15811 //===========================================================================================================================
15812
15813 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
15814 {
15815 switch( inIfIndex )
15816 {
15817 case kDNSServiceInterfaceIndexAny:
15818 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
15819 break;
15820
15821 case kDNSServiceInterfaceIndexLocalOnly:
15822 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
15823 break;
15824
15825 case kDNSServiceInterfaceIndexUnicast:
15826 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
15827 break;
15828
15829 case kDNSServiceInterfaceIndexP2P:
15830 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
15831 break;
15832
15833 #if( defined( kDNSServiceInterfaceIndexBLE ) )
15834 case kDNSServiceInterfaceIndexBLE:
15835 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
15836 break;
15837 #endif
15838
15839 default:
15840 {
15841 const char * name;
15842
15843 name = if_indextoname( inIfIndex, inNameBuf );
15844 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
15845 break;
15846 }
15847 }
15848
15849 return( inNameBuf );
15850 }
15851
15852 //===========================================================================================================================
15853 // RecordTypeToString
15854 //===========================================================================================================================
15855
15856 static const char * RecordTypeToString( unsigned int inValue )
15857 {
15858 const RecordType * type;
15859 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
15860
15861 for( type = kRecordTypes; type < end; ++type )
15862 {
15863 if( type->value == inValue ) return( type->name );
15864 }
15865 return( "???" );
15866 }
15867
15868 //===========================================================================================================================
15869 // DNSMessageExtractDomainName
15870 //===========================================================================================================================
15871
15872 static OSStatus
15873 DNSMessageExtractDomainName(
15874 const uint8_t * inMsgPtr,
15875 size_t inMsgLen,
15876 const uint8_t * inNamePtr,
15877 uint8_t inBuf[ kDomainNameLengthMax ],
15878 const uint8_t ** outNextPtr )
15879 {
15880 OSStatus err;
15881 const uint8_t * label;
15882 uint8_t labelLen;
15883 const uint8_t * nextLabel;
15884 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
15885 uint8_t * dst = inBuf;
15886 const uint8_t * const dstLim = inBuf ? ( inBuf + kDomainNameLengthMax ) : NULL;
15887 const uint8_t * nameEnd = NULL;
15888
15889 require_action( ( inNamePtr >= inMsgPtr ) && ( inNamePtr < msgEnd ), exit, err = kRangeErr );
15890
15891 for( label = inNamePtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
15892 {
15893 if( labelLen <= kDomainLabelLengthMax )
15894 {
15895 nextLabel = label + 1 + labelLen;
15896 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
15897 if( dst )
15898 {
15899 require_action( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
15900 memcpy( dst, label, 1 + labelLen );
15901 dst += ( 1 + labelLen );
15902 }
15903 }
15904 else if( IsCompressionByte( labelLen ) )
15905 {
15906 uint16_t offset;
15907
15908 require_action( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
15909 if( !nameEnd )
15910 {
15911 nameEnd = label + 2;
15912 if( !dst ) break;
15913 }
15914 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
15915 nextLabel = inMsgPtr + offset;
15916 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
15917 require_action( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
15918 }
15919 else
15920 {
15921 dlogassert( "Unhandled label length 0x%02X\n", labelLen );
15922 err = kMalformedErr;
15923 goto exit;
15924 }
15925 }
15926
15927 if( dst ) *dst = 0;
15928 if( !nameEnd ) nameEnd = label + 1;
15929
15930 if( outNextPtr ) *outNextPtr = nameEnd;
15931 err = kNoErr;
15932
15933 exit:
15934 return( err );
15935 }
15936
15937 //===========================================================================================================================
15938 // DNSMessageExtractDomainNameString
15939 //===========================================================================================================================
15940
15941 static OSStatus
15942 DNSMessageExtractDomainNameString(
15943 const void * inMsgPtr,
15944 size_t inMsgLen,
15945 const void * inNamePtr,
15946 char inBuf[ kDNSServiceMaxDomainName ],
15947 const uint8_t ** outNextPtr )
15948 {
15949 OSStatus err;
15950 const uint8_t * nextPtr;
15951 uint8_t domainName[ kDomainNameLengthMax ];
15952
15953 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inNamePtr, domainName, &nextPtr );
15954 require_noerr( err, exit );
15955
15956 err = DomainNameToString( domainName, NULL, inBuf, NULL );
15957 require_noerr( err, exit );
15958
15959 if( outNextPtr ) *outNextPtr = nextPtr;
15960
15961 exit:
15962 return( err );
15963 }
15964
15965 //===========================================================================================================================
15966 // DNSMessageExtractQuestion
15967 //===========================================================================================================================
15968
15969 static OSStatus
15970 DNSMessageExtractQuestion(
15971 const uint8_t * inMsgPtr,
15972 size_t inMsgLen,
15973 const uint8_t * inPtr,
15974 uint8_t inNameBuf[ kDomainNameLengthMax ],
15975 uint16_t * outType,
15976 uint16_t * outClass,
15977 const uint8_t ** outPtr )
15978 {
15979 OSStatus err;
15980 const uint8_t * const msgEnd = &inMsgPtr[ inMsgLen ];
15981 const uint8_t * ptr;
15982 const DNSQuestionFixedFields * fields;
15983
15984 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr );
15985 require_noerr_quiet( err, exit );
15986 require_action_quiet( (size_t)( msgEnd - ptr ) >= sizeof( DNSQuestionFixedFields ), exit, err = kUnderrunErr );
15987
15988 fields = (const DNSQuestionFixedFields *) ptr;
15989 if( outType ) *outType = DNSQuestionFixedFieldsGetType( fields );
15990 if( outClass ) *outClass = DNSQuestionFixedFieldsGetClass( fields );
15991 if( outPtr ) *outPtr = (const uint8_t *) &fields[ 1 ];
15992
15993 exit:
15994 return( err );
15995 }
15996
15997 //===========================================================================================================================
15998 // DNSMessageExtractRecord
15999 //===========================================================================================================================
16000
16001 typedef struct
16002 {
16003 uint8_t type[ 2 ];
16004 uint8_t class[ 2 ];
16005 uint8_t ttl[ 4 ];
16006 uint8_t rdLength[ 2 ];
16007 uint8_t rdata[ 1 ];
16008
16009 } DNSRecordFields;
16010
16011 check_compile_time( offsetof( DNSRecordFields, rdata ) == 10 );
16012
16013 static OSStatus
16014 DNSMessageExtractRecord(
16015 const uint8_t * inMsgPtr,
16016 size_t inMsgLen,
16017 const uint8_t * inPtr,
16018 uint8_t inNameBuf[ kDomainNameLengthMax ],
16019 uint16_t * outType,
16020 uint16_t * outClass,
16021 uint32_t * outTTL,
16022 const uint8_t ** outRDataPtr,
16023 size_t * outRDataLen,
16024 const uint8_t ** outPtr )
16025 {
16026 OSStatus err;
16027 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
16028 const uint8_t * ptr;
16029 const DNSRecordFields * record;
16030 size_t rdLength;
16031
16032 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr );
16033 require_noerr_quiet( err, exit );
16034 require_action_quiet( (size_t)( msgEnd - ptr ) >= offsetof( DNSRecordFields, rdata ), exit, err = kUnderrunErr );
16035
16036 record = (DNSRecordFields *) ptr;
16037 rdLength = ReadBig16( record->rdLength );
16038 require_action_quiet( (size_t)( msgEnd - record->rdata ) >= rdLength , exit, err = kUnderrunErr );
16039
16040 if( outType ) *outType = ReadBig16( record->type );
16041 if( outClass ) *outClass = ReadBig16( record->class );
16042 if( outTTL ) *outTTL = ReadBig32( record->ttl );
16043 if( outRDataPtr ) *outRDataPtr = record->rdata;
16044 if( outRDataLen ) *outRDataLen = rdLength;
16045 if( outPtr ) *outPtr = record->rdata + rdLength;
16046
16047 exit:
16048 return( err );
16049 }
16050
16051 //===========================================================================================================================
16052 // DNSMessageGetAnswerSection
16053 //===========================================================================================================================
16054
16055 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
16056 {
16057 OSStatus err;
16058 unsigned int questionCount, i;
16059 const DNSHeader * hdr;
16060 const uint8_t * ptr;
16061
16062 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
16063
16064 hdr = (DNSHeader *) inMsgPtr;
16065 questionCount = DNSHeaderGetQuestionCount( hdr );
16066
16067 ptr = (const uint8_t *) &hdr[ 1 ];
16068 for( i = 0; i < questionCount; ++i )
16069 {
16070 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, NULL, NULL, NULL, &ptr );
16071 require_noerr( err, exit );
16072 }
16073
16074 if( outPtr ) *outPtr = ptr;
16075 err = kNoErr;
16076
16077 exit:
16078 return( err );
16079 }
16080
16081 //===========================================================================================================================
16082 // DNSRecordDataToString
16083 //===========================================================================================================================
16084
16085 static OSStatus
16086 DNSRecordDataToString(
16087 const void * inRDataPtr,
16088 size_t inRDataLen,
16089 unsigned int inRDataType,
16090 const void * inMsgPtr,
16091 size_t inMsgLen,
16092 char ** outString )
16093 {
16094 OSStatus err;
16095 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
16096 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
16097 char * rdataStr;
16098 const uint8_t * ptr;
16099 int n;
16100 char domainNameStr[ kDNSServiceMaxDomainName ];
16101
16102 rdataStr = NULL;
16103 if( inRDataType == kDNSServiceType_A )
16104 {
16105 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
16106
16107 ASPrintF( &rdataStr, "%.4a", rdataPtr );
16108 require_action( rdataStr, exit, err = kNoMemoryErr );
16109 }
16110 else if( inRDataType == kDNSServiceType_AAAA )
16111 {
16112 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
16113
16114 ASPrintF( &rdataStr, "%.16a", rdataPtr );
16115 require_action( rdataStr, exit, err = kNoMemoryErr );
16116 }
16117 else if( ( inRDataType == kDNSServiceType_PTR ) || ( inRDataType == kDNSServiceType_CNAME ) ||
16118 ( inRDataType == kDNSServiceType_NS ) )
16119 {
16120 if( inMsgPtr )
16121 {
16122 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
16123 require_noerr( err, exit );
16124 }
16125 else
16126 {
16127 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
16128 require_noerr( err, exit );
16129 }
16130
16131 rdataStr = strdup( domainNameStr );
16132 require_action( rdataStr, exit, err = kNoMemoryErr );
16133 }
16134 else if( inRDataType == kDNSServiceType_SRV )
16135 {
16136 const SRVRecordDataFixedFields * fields;
16137 const uint8_t * target;
16138 unsigned int priority, weight, port;
16139
16140 require_action_quiet( inRDataLen > sizeof( SRVRecordDataFixedFields ), exit, err = kMalformedErr );
16141
16142 fields = (const SRVRecordDataFixedFields *) rdataPtr;
16143 SRVRecordDataFixedFieldsGet( fields, &priority, &weight, &port );
16144 target = (const uint8_t *) &fields[ 1 ];
16145
16146 if( inMsgPtr )
16147 {
16148 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
16149 require_noerr( err, exit );
16150 }
16151 else
16152 {
16153 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
16154 require_noerr( err, exit );
16155 }
16156
16157 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
16158 require_action( rdataStr, exit, err = kNoMemoryErr );
16159 }
16160 else if( inRDataType == kDNSServiceType_TXT )
16161 {
16162 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
16163
16164 if( inRDataLen == 1 )
16165 {
16166 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
16167 require_action( rdataStr, exit, err = kNoMemoryErr );
16168 }
16169 else
16170 {
16171 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
16172 require_action( rdataStr, exit, err = kNoMemoryErr );
16173 }
16174 }
16175 else if( inRDataType == kDNSServiceType_SOA )
16176 {
16177 uint32_t serial, refresh, retry, expire, minimum;
16178
16179 if( inMsgPtr )
16180 {
16181 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
16182 require_noerr( err, exit );
16183
16184 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
16185
16186 rdataStr = strdup( domainNameStr );
16187 require_action( rdataStr, exit, err = kNoMemoryErr );
16188
16189 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
16190 require_noerr( err, exit );
16191 }
16192 else
16193 {
16194 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
16195 require_noerr( err, exit );
16196
16197 rdataStr = strdup( domainNameStr );
16198 require_action( rdataStr, exit, err = kNoMemoryErr );
16199
16200 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
16201 require_noerr( err, exit );
16202 }
16203
16204 require_action_quiet( ( rdataEnd - ptr ) == sizeof( SOARecordDataFixedFields ), exit, err = kMalformedErr );
16205
16206 SOARecordDataFixedFieldsGet( (const SOARecordDataFixedFields *) ptr, &serial, &refresh, &retry, &expire, &minimum );
16207
16208 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
16209 require_action( n > 0, exit, err = kUnknownErr );
16210 }
16211 else if( inRDataType == kDNSServiceType_NSEC )
16212 {
16213 unsigned int windowBlock, bitmapLen, i, recordType;
16214 const uint8_t * bitmapPtr;
16215
16216 if( inMsgPtr )
16217 {
16218 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
16219 require_noerr( err, exit );
16220 }
16221 else
16222 {
16223 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
16224 require_noerr( err, exit );
16225 }
16226
16227 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
16228
16229 rdataStr = strdup( domainNameStr );
16230 require_action( rdataStr, exit, err = kNoMemoryErr );
16231
16232 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
16233 {
16234 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
16235
16236 windowBlock = ptr[ 0 ];
16237 bitmapLen = ptr[ 1 ];
16238 bitmapPtr = &ptr[ 2 ];
16239
16240 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
16241 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
16242
16243 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
16244 {
16245 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
16246 {
16247 recordType = ( windowBlock * 256 ) + i;
16248 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
16249 require_action( n > 0, exit, err = kUnknownErr );
16250 }
16251 }
16252 }
16253 }
16254 else if( inRDataType == kDNSServiceType_MX )
16255 {
16256 uint16_t preference;
16257 const uint8_t * exchange;
16258
16259 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
16260
16261 preference = ReadBig16( rdataPtr );
16262 exchange = &rdataPtr[ 2 ];
16263
16264 if( inMsgPtr )
16265 {
16266 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
16267 require_noerr( err, exit );
16268 }
16269 else
16270 {
16271 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
16272 require_noerr( err, exit );
16273 }
16274
16275 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
16276 require_action( n > 0, exit, err = kUnknownErr );
16277 }
16278 else
16279 {
16280 err = kNotHandledErr;
16281 goto exit;
16282 }
16283
16284 check( rdataStr );
16285 *outString = rdataStr;
16286 rdataStr = NULL;
16287 err = kNoErr;
16288
16289 exit:
16290 FreeNullSafe( rdataStr );
16291 return( err );
16292 }
16293
16294 //===========================================================================================================================
16295 // DomainNameAppendString
16296 //===========================================================================================================================
16297
16298 static OSStatus
16299 DomainNameAppendString(
16300 uint8_t inDomainName[ kDomainNameLengthMax ],
16301 const char * inString,
16302 uint8_t ** outEndPtr )
16303 {
16304 OSStatus err;
16305 const char * src;
16306 uint8_t * root;
16307 const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax;
16308
16309 for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {}
16310 require_action_quiet( root < nameLim, exit, err = kMalformedErr );
16311
16312 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
16313
16314 src = inString;
16315 if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src;
16316 while( *src )
16317 {
16318 uint8_t * const label = root;
16319 const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 );
16320 uint8_t * dst;
16321 int c;
16322 size_t labelLen;
16323
16324 dst = &label[ 1 ];
16325 while( *src && ( ( c = *src++ ) != '.' ) )
16326 {
16327 if( c == '\\' )
16328 {
16329 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
16330 c = *src++;
16331 if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
16332 {
16333 const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
16334
16335 if( decimal <= 255 )
16336 {
16337 c = decimal;
16338 src += 2;
16339 }
16340 }
16341 }
16342 require_action_quiet( dst < labelLim, exit, err = kOverrunErr );
16343 *dst++ = (uint8_t) c;
16344 }
16345
16346 labelLen = (size_t)( dst - &label[ 1 ] );
16347 require_action_quiet( labelLen > 0, exit, err = kMalformedErr );
16348
16349 label[ 0 ] = (uint8_t) labelLen;
16350 root = dst;
16351 *root = 0;
16352 }
16353
16354 if( outEndPtr ) *outEndPtr = root + 1;
16355 err = kNoErr;
16356
16357 exit:
16358 return( err );
16359 }
16360
16361 //===========================================================================================================================
16362 // DomainNameEqual
16363 //===========================================================================================================================
16364
16365 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
16366 {
16367 const uint8_t * p1 = inName1;
16368 const uint8_t * p2 = inName2;
16369 unsigned int len;
16370
16371 for( ;; )
16372 {
16373 if( ( len = *p1++ ) != *p2++ ) return( false );
16374 if( len == 0 ) break;
16375 for( ; len > 0; ++p1, ++p2, --len )
16376 {
16377 if( tolower_safe( *p1 ) != tolower_safe( *p2 ) ) return( false );
16378 }
16379 }
16380 return( true );
16381 }
16382
16383 //===========================================================================================================================
16384 // DomainNameLength
16385 //===========================================================================================================================
16386
16387 static size_t DomainNameLength( const uint8_t * const inName )
16388 {
16389 const uint8_t * ptr;
16390
16391 for( ptr = inName; *ptr != 0; ptr += ( 1 + *ptr ) ) {}
16392 return( (size_t)( ptr - inName ) + 1 );
16393 }
16394
16395 //===========================================================================================================================
16396 // DomainNameDupEx
16397 //===========================================================================================================================
16398
16399 static OSStatus DomainNameDupEx( const uint8_t *inName, Boolean inLower, uint8_t **outNamePtr, size_t *outNameLen )
16400 {
16401 OSStatus err;
16402 uint8_t * namePtr;
16403 const size_t nameLen = DomainNameLength( inName );
16404
16405 if( inLower )
16406 {
16407 const uint8_t * src;
16408 uint8_t * dst;
16409 unsigned int len;
16410
16411 namePtr = (uint8_t *) malloc( nameLen );
16412 require_action( namePtr, exit, err = kNoMemoryErr );
16413
16414 src = inName;
16415 dst = namePtr;
16416 while( ( len = *src ) != 0 )
16417 {
16418 *dst++ = *src++;
16419 while( len-- )
16420 {
16421 *dst++ = (uint8_t) tolower_safe( *src );
16422 ++src;
16423 }
16424 }
16425 *dst = 0;
16426 }
16427 else
16428 {
16429 namePtr = (uint8_t *) memdup( inName, nameLen );
16430 require_action( namePtr, exit, err = kNoMemoryErr );
16431 }
16432
16433 *outNamePtr = namePtr;
16434 if( outNameLen ) *outNameLen = nameLen;
16435 err = kNoErr;
16436
16437 exit:
16438 return( err );
16439 }
16440
16441 //===========================================================================================================================
16442 // DomainNameFromString
16443 //===========================================================================================================================
16444
16445 static OSStatus
16446 DomainNameFromString(
16447 uint8_t inDomainName[ kDomainNameLengthMax ],
16448 const char * inString,
16449 uint8_t ** outEndPtr )
16450 {
16451 inDomainName[ 0 ] = 0;
16452 return( DomainNameAppendString( inDomainName, inString, outEndPtr ) );
16453 }
16454
16455 //===========================================================================================================================
16456 // DomainNameToString
16457 //===========================================================================================================================
16458
16459 static OSStatus
16460 DomainNameToString(
16461 const uint8_t * inDomainName,
16462 const uint8_t * inEnd,
16463 char inBuf[ kDNSServiceMaxDomainName ],
16464 const uint8_t ** outNextPtr )
16465 {
16466 OSStatus err;
16467 const uint8_t * label;
16468 uint8_t labelLen;
16469 const uint8_t * nextLabel;
16470 char * dst;
16471 const uint8_t * src;
16472
16473 require_action( !inEnd || ( inDomainName < inEnd ), exit, err = kUnderrunErr );
16474
16475 // Convert each label up until the root label, i.e., the zero-length label.
16476
16477 dst = inBuf;
16478 for( label = inDomainName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
16479 {
16480 require_action( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
16481
16482 nextLabel = &label[ 1 ] + labelLen;
16483 require_action( ( nextLabel - inDomainName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
16484 require_action( !inEnd || ( nextLabel < inEnd ), exit, err = kUnderrunErr );
16485
16486 for( src = &label[ 1 ]; src < nextLabel; ++src )
16487 {
16488 if( isprint_safe( *src ) )
16489 {
16490 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
16491 *dst++ = (char) *src;
16492 }
16493 else
16494 {
16495 *dst++ = '\\';
16496 *dst++ = '0' + ( *src / 100 );
16497 *dst++ = '0' + ( ( *src / 10 ) % 10 );
16498 *dst++ = '0' + ( *src % 10 );
16499 }
16500 }
16501 *dst++ = '.';
16502 }
16503
16504 // At this point, label points to the root label.
16505 // If the root label was the only label, then write a dot for it.
16506
16507 if( label == inDomainName ) *dst++ = '.';
16508 *dst = '\0';
16509 if( outNextPtr ) *outNextPtr = label + 1;
16510 err = kNoErr;
16511
16512 exit:
16513 return( err );
16514 }
16515
16516 //===========================================================================================================================
16517 // DNSMessageToText
16518 //===========================================================================================================================
16519
16520 #define DNSFlagsOpCodeToString( X ) ( \
16521 ( (X) == kDNSOpCode_Query ) ? "Query" : \
16522 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
16523 ( (X) == kDNSOpCode_Status ) ? "Status" : \
16524 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
16525 ( (X) == kDNSOpCode_Update ) ? "Update" : \
16526 "Unassigned" )
16527
16528 #define DNSFlagsRCodeToString( X ) ( \
16529 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
16530 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
16531 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
16532 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
16533 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
16534 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
16535 "???" )
16536
16537 static OSStatus
16538 DNSMessageToText(
16539 const uint8_t * inMsgPtr,
16540 size_t inMsgLen,
16541 const Boolean inMDNS,
16542 const Boolean inPrintRaw,
16543 char ** outText )
16544 {
16545 OSStatus err;
16546 DataBuffer dataBuf;
16547 size_t len;
16548 const DNSHeader * hdr;
16549 const uint8_t * ptr;
16550 unsigned int id, flags, opcode, rcode;
16551 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
16552 uint8_t name[ kDomainNameLengthMax ];
16553 char nameStr[ kDNSServiceMaxDomainName ];
16554
16555 DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
16556 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
16557
16558 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
16559
16560 hdr = (DNSHeader *) inMsgPtr;
16561 id = DNSHeaderGetID( hdr );
16562 flags = DNSHeaderGetFlags( hdr );
16563 questionCount = DNSHeaderGetQuestionCount( hdr );
16564 answerCount = DNSHeaderGetAnswerCount( hdr );
16565 authorityCount = DNSHeaderGetAuthorityCount( hdr );
16566 additionalCount = DNSHeaderGetAdditionalCount( hdr );
16567 opcode = DNSFlagsGetOpCode( flags );
16568 rcode = DNSFlagsGetRCode( flags );
16569
16570 _Append( "ID: 0x%04X (%u)\n", id, id );
16571 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
16572 flags,
16573 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
16574 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
16575 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
16576 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
16577 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
16578 !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD",
16579 !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD",
16580 DNSFlagsRCodeToString( rcode ) );
16581 _Append( "Question count: %u\n", questionCount );
16582 _Append( "Answer count: %u\n", answerCount );
16583 _Append( "Authority count: %u\n", authorityCount );
16584 _Append( "Additional count: %u\n", additionalCount );
16585
16586 ptr = (const uint8_t *) &hdr[ 1 ];
16587 for( i = 0; i < questionCount; ++i )
16588 {
16589 uint16_t qtype, qclass;
16590 Boolean isQU;
16591
16592 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
16593 require_noerr( err, exit );
16594
16595 err = DomainNameToString( name, NULL, nameStr, NULL );
16596 require_noerr( err, exit );
16597
16598 isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
16599 if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
16600
16601 if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
16602
16603 _Append( "%-30s %2s %?2s%?2u %-5s\n",
16604 nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
16605 ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
16606 }
16607
16608 totalRRCount = answerCount + authorityCount + additionalCount;
16609 for( i = 0; i < totalRRCount; ++i )
16610 {
16611 uint16_t type;
16612 uint16_t class;
16613 uint32_t ttl;
16614 const uint8_t * rdataPtr;
16615 size_t rdataLen;
16616 char * rdataStr;
16617 Boolean cacheFlush;
16618
16619 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
16620 require_noerr( err, exit );
16621
16622 err = DomainNameToString( name, NULL, nameStr, NULL );
16623 require_noerr( err, exit );
16624
16625 cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
16626 if( inMDNS ) class &= ~kRRClassCacheFlushBit;
16627
16628 rdataStr = NULL;
16629 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
16630 if( !rdataStr )
16631 {
16632 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, INT_MAX );
16633 require_action( rdataStr, exit, err = kNoMemoryErr );
16634 }
16635
16636 if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" );
16637 else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" );
16638 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
16639
16640 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
16641 nameStr, ttl, cacheFlush ? "CF" : "",
16642 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
16643 RecordTypeToString( type ), rdataStr );
16644 free( rdataStr );
16645 }
16646 _Append( "\n" );
16647
16648 err = DataBuffer_Append( &dataBuf, "", 1 );
16649 require_noerr( err, exit );
16650
16651 err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
16652 require_noerr( err, exit );
16653
16654 exit:
16655 DataBuffer_Free( &dataBuf );
16656 return( err );
16657 }
16658
16659 //===========================================================================================================================
16660 // WriteDNSQueryMessage
16661 //===========================================================================================================================
16662
16663 static OSStatus
16664 WriteDNSQueryMessage(
16665 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
16666 uint16_t inMsgID,
16667 uint16_t inFlags,
16668 const char * inQName,
16669 uint16_t inQType,
16670 uint16_t inQClass,
16671 size_t * outMsgLen )
16672 {
16673 OSStatus err;
16674 DNSHeader * const hdr = (DNSHeader *) inMsg;
16675 uint8_t * ptr;
16676 size_t msgLen;
16677
16678 memset( hdr, 0, sizeof( *hdr ) );
16679 DNSHeaderSetID( hdr, inMsgID );
16680 DNSHeaderSetFlags( hdr, inFlags );
16681 DNSHeaderSetQuestionCount( hdr, 1 );
16682
16683 ptr = (uint8_t *)( hdr + 1 );
16684 err = DomainNameFromString( ptr, inQName, &ptr );
16685 require_noerr_quiet( err, exit );
16686
16687 DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields *) ptr, inQType, inQClass );
16688 ptr += 4;
16689
16690 msgLen = (size_t)( ptr - inMsg );
16691 check( msgLen <= kDNSQueryMessageMaxLen );
16692
16693 if( outMsgLen ) *outMsgLen = msgLen;
16694
16695 exit:
16696 return( err );
16697 }
16698
16699 //===========================================================================================================================
16700 // DispatchSignalSourceCreate
16701 //===========================================================================================================================
16702
16703 static OSStatus
16704 DispatchSignalSourceCreate(
16705 int inSignal,
16706 DispatchHandler inEventHandler,
16707 void * inContext,
16708 dispatch_source_t * outSource )
16709 {
16710 OSStatus err;
16711 dispatch_source_t source;
16712
16713 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
16714 require_action( source, exit, err = kUnknownErr );
16715
16716 dispatch_set_context( source, inContext );
16717 dispatch_source_set_event_handler_f( source, inEventHandler );
16718
16719 *outSource = source;
16720 err = kNoErr;
16721
16722 exit:
16723 return( err );
16724 }
16725
16726 //===========================================================================================================================
16727 // DispatchSocketSourceCreate
16728 //===========================================================================================================================
16729
16730 static OSStatus
16731 DispatchSocketSourceCreate(
16732 SocketRef inSock,
16733 dispatch_source_type_t inType,
16734 dispatch_queue_t inQueue,
16735 DispatchHandler inEventHandler,
16736 DispatchHandler inCancelHandler,
16737 void * inContext,
16738 dispatch_source_t * outSource )
16739 {
16740 OSStatus err;
16741 dispatch_source_t source;
16742
16743 source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
16744 require_action( source, exit, err = kUnknownErr );
16745
16746 dispatch_set_context( source, inContext );
16747 dispatch_source_set_event_handler_f( source, inEventHandler );
16748 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
16749
16750 *outSource = source;
16751 err = kNoErr;
16752
16753 exit:
16754 return( err );
16755 }
16756
16757 //===========================================================================================================================
16758 // DispatchTimerCreate
16759 //===========================================================================================================================
16760
16761 static OSStatus
16762 DispatchTimerCreate(
16763 dispatch_time_t inStart,
16764 uint64_t inIntervalNs,
16765 uint64_t inLeewayNs,
16766 dispatch_queue_t inQueue,
16767 DispatchHandler inEventHandler,
16768 DispatchHandler inCancelHandler,
16769 void * inContext,
16770 dispatch_source_t * outTimer )
16771 {
16772 OSStatus err;
16773 dispatch_source_t timer;
16774
16775 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
16776 require_action( timer, exit, err = kUnknownErr );
16777
16778 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
16779 dispatch_set_context( timer, inContext );
16780 dispatch_source_set_event_handler_f( timer, inEventHandler );
16781 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
16782
16783 *outTimer = timer;
16784 err = kNoErr;
16785
16786 exit:
16787 return( err );
16788 }
16789
16790 //===========================================================================================================================
16791 // DispatchProcessMonitorCreate
16792 //===========================================================================================================================
16793
16794 static OSStatus
16795 DispatchProcessMonitorCreate(
16796 pid_t inPID,
16797 unsigned long inFlags,
16798 dispatch_queue_t inQueue,
16799 DispatchHandler inEventHandler,
16800 DispatchHandler inCancelHandler,
16801 void * inContext,
16802 dispatch_source_t * outMonitor )
16803 {
16804 OSStatus err;
16805 dispatch_source_t monitor;
16806
16807 monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
16808 inQueue ? inQueue : dispatch_get_main_queue() );
16809 require_action( monitor, exit, err = kUnknownErr );
16810
16811 dispatch_set_context( monitor, inContext );
16812 dispatch_source_set_event_handler_f( monitor, inEventHandler );
16813 dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
16814
16815 *outMonitor = monitor;
16816 err = kNoErr;
16817
16818 exit:
16819 return( err );
16820 }
16821
16822 //===========================================================================================================================
16823 // ServiceTypeDescription
16824 //===========================================================================================================================
16825
16826 typedef struct
16827 {
16828 const char * name; // Name of the service type in two-label "_service._proto" format.
16829 const char * description; // Description of the service type.
16830
16831 } ServiceType;
16832
16833 // A Non-comprehensive table of DNS-SD service types
16834
16835 static const ServiceType kServiceTypes[] =
16836 {
16837 { "_acp-sync._tcp", "AirPort Base Station Sync" },
16838 { "_adisk._tcp", "Automatic Disk Discovery" },
16839 { "_afpovertcp._tcp", "Apple File Sharing" },
16840 { "_airdrop._tcp", "AirDrop" },
16841 { "_airplay._tcp", "AirPlay" },
16842 { "_airport._tcp", "AirPort Base Station" },
16843 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
16844 { "_eppc._tcp", "Remote AppleEvents" },
16845 { "_ftp._tcp", "File Transfer Protocol" },
16846 { "_home-sharing._tcp", "Home Sharing" },
16847 { "_homekit._tcp", "HomeKit" },
16848 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
16849 { "_https._tcp", "HTTP over SSL/TLS" },
16850 { "_ipp._tcp", "Internet Printing Protocol" },
16851 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
16852 { "_mediaremotetv._tcp", "Media Remote" },
16853 { "_net-assistant._tcp", "Apple Remote Desktop" },
16854 { "_od-master._tcp", "OpenDirectory Master" },
16855 { "_nfs._tcp", "Network File System" },
16856 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
16857 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
16858 { "_raop._tcp", "Remote Audio Output Protocol" },
16859 { "_rfb._tcp", "Remote Frame Buffer" },
16860 { "_scanner._tcp", "Bonjour Scanning" },
16861 { "_smb._tcp", "Server Message Block over TCP/IP" },
16862 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
16863 { "_sleep-proxy._udp", "Sleep Proxy Server" },
16864 { "_ssh._tcp", "SSH Remote Login Protocol" },
16865 { "_teleport._tcp", "teleport" },
16866 { "_tftp._tcp", "Trivial File Transfer Protocol" },
16867 { "_workstation._tcp", "Workgroup Manager" },
16868 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
16869 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
16870 };
16871
16872 static const char * ServiceTypeDescription( const char *inName )
16873 {
16874 const ServiceType * serviceType;
16875 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
16876
16877 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
16878 {
16879 if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
16880 {
16881 const size_t len = strlen( serviceType->name );
16882
16883 if( ( inName[ len ] == '\0' ) || ( strcmp( &inName[ len ], "." ) == 0 ) )
16884 {
16885 return( serviceType->description );
16886 }
16887 }
16888 }
16889 return( NULL );
16890 }
16891
16892 //===========================================================================================================================
16893 // SocketContextCreate
16894 //===========================================================================================================================
16895
16896 static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
16897 {
16898 OSStatus err;
16899 SocketContext * context;
16900
16901 context = (SocketContext *) calloc( 1, sizeof( *context ) );
16902 require_action( context, exit, err = kNoMemoryErr );
16903
16904 context->refCount = 1;
16905 context->sock = inSock;
16906 context->userContext = inUserContext;
16907
16908 *outContext = context;
16909 err = kNoErr;
16910
16911 exit:
16912 return( err );
16913 }
16914
16915 //===========================================================================================================================
16916 // SocketContextRetain
16917 //===========================================================================================================================
16918
16919 static SocketContext * SocketContextRetain( SocketContext *inContext )
16920 {
16921 ++inContext->refCount;
16922 return( inContext );
16923 }
16924
16925 //===========================================================================================================================
16926 // SocketContextRelease
16927 //===========================================================================================================================
16928
16929 static void SocketContextRelease( SocketContext *inContext )
16930 {
16931 if( --inContext->refCount == 0 )
16932 {
16933 ForgetSocket( &inContext->sock );
16934 free( inContext );
16935 }
16936 }
16937
16938 //===========================================================================================================================
16939 // SocketContextCancelHandler
16940 //===========================================================================================================================
16941
16942 static void SocketContextCancelHandler( void *inContext )
16943 {
16944 SocketContextRelease( (SocketContext *) inContext );
16945 }
16946
16947 //===========================================================================================================================
16948 // StringToInt32
16949 //===========================================================================================================================
16950
16951 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
16952 {
16953 OSStatus err;
16954 long value;
16955 char * endPtr;
16956
16957 value = strtol( inString, &endPtr, 0 );
16958 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
16959 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
16960
16961 *outValue = (int32_t) value;
16962 err = kNoErr;
16963
16964 exit:
16965 return( err );
16966 }
16967
16968 //===========================================================================================================================
16969 // StringToUInt32
16970 //===========================================================================================================================
16971
16972 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
16973 {
16974 OSStatus err;
16975 uint32_t value;
16976 char * endPtr;
16977
16978 value = (uint32_t) strtol( inString, &endPtr, 0 );
16979 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
16980
16981 *outValue = value;
16982 err = kNoErr;
16983
16984 exit:
16985 return( err );
16986 }
16987
16988 #if( TARGET_OS_DARWIN )
16989 //===========================================================================================================================
16990 // StringToPID
16991 //===========================================================================================================================
16992
16993 static OSStatus StringToPID( const char *inString, pid_t *outPID )
16994 {
16995 OSStatus err;
16996 long long value;
16997 char * endPtr;
16998
16999 set_errno_compat( 0 );
17000 value = strtoll( inString, &endPtr, 0 );
17001 err = errno_compat();
17002 require_noerr_quiet( err, exit );
17003 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kMalformedErr );
17004 require_action_quiet( value == (pid_t) value, exit, err = kRangeErr );
17005
17006 *outPID = (pid_t) value;
17007 err = kNoErr;
17008
17009 exit:
17010 return( err );
17011 }
17012 #endif
17013
17014 //===========================================================================================================================
17015 // StringToARecordData
17016 //===========================================================================================================================
17017
17018 static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
17019 {
17020 OSStatus err;
17021 uint32_t * addrPtr;
17022 const size_t addrLen = sizeof( *addrPtr );
17023 const char * end;
17024
17025 addrPtr = (uint32_t *) malloc( addrLen );
17026 require_action( addrPtr, exit, err = kNoMemoryErr );
17027
17028 err = StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
17029 NULL, NULL, NULL, &end );
17030 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
17031 require_noerr_quiet( err, exit );
17032
17033 *addrPtr = HostToBig32( *addrPtr );
17034
17035 *outPtr = (uint8_t *) addrPtr;
17036 addrPtr = NULL;
17037 *outLen = addrLen;
17038
17039 exit:
17040 FreeNullSafe( addrPtr );
17041 return( err );
17042 }
17043
17044 //===========================================================================================================================
17045 // StringToAAAARecordData
17046 //===========================================================================================================================
17047
17048 static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
17049 {
17050 OSStatus err;
17051 uint8_t * addrPtr;
17052 const size_t addrLen = 16;
17053 const char * end;
17054
17055 addrPtr = (uint8_t *) malloc( addrLen );
17056 require_action( addrPtr, exit, err = kNoMemoryErr );
17057
17058 err = StringToIPv6Address( inString,
17059 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
17060 addrPtr, NULL, NULL, NULL, &end );
17061 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
17062 require_noerr_quiet( err, exit );
17063
17064 *outPtr = addrPtr;
17065 addrPtr = NULL;
17066 *outLen = addrLen;
17067
17068 exit:
17069 FreeNullSafe( addrPtr );
17070 return( err );
17071 }
17072
17073 //===========================================================================================================================
17074 // StringToDomainName
17075 //===========================================================================================================================
17076
17077 static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
17078 {
17079 OSStatus err;
17080 uint8_t * namePtr;
17081 size_t nameLen;
17082 uint8_t * end;
17083 uint8_t nameBuf[ kDomainNameLengthMax ];
17084
17085 err = DomainNameFromString( nameBuf, inString, &end );
17086 require_noerr_quiet( err, exit );
17087
17088 nameLen = (size_t)( end - nameBuf );
17089 namePtr = memdup( nameBuf, nameLen );
17090 require_action( namePtr, exit, err = kNoMemoryErr );
17091
17092 *outPtr = namePtr;
17093 namePtr = NULL;
17094 if( outLen ) *outLen = nameLen;
17095
17096 exit:
17097 return( err );
17098 }
17099
17100 #if( TARGET_OS_DARWIN )
17101 //===========================================================================================================================
17102 // GetDefaultDNSServer
17103 //===========================================================================================================================
17104
17105 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
17106 {
17107 OSStatus err;
17108 dns_config_t * config;
17109 struct sockaddr * addr;
17110 int32_t i;
17111
17112 config = dns_configuration_copy();
17113 require_action( config, exit, err = kUnknownErr );
17114
17115 addr = NULL;
17116 for( i = 0; i < config->n_resolver; ++i )
17117 {
17118 const dns_resolver_t * const resolver = config->resolver[ i ];
17119
17120 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
17121 {
17122 addr = resolver->nameserver[ 0 ];
17123 break;
17124 }
17125 }
17126 require_action_quiet( addr, exit, err = kNotFoundErr );
17127
17128 SockAddrCopy( addr, outAddr );
17129 err = kNoErr;
17130
17131 exit:
17132 if( config ) dns_configuration_free( config );
17133 return( err );
17134 }
17135 #endif
17136
17137 //===========================================================================================================================
17138 // GetMDNSMulticastAddrV4
17139 //===========================================================================================================================
17140
17141 static void _MDNSMulticastAddrV4Init( void *inContext );
17142
17143 static const struct sockaddr * GetMDNSMulticastAddrV4( void )
17144 {
17145 static struct sockaddr_in sMDNSMulticastAddrV4;
17146 static dispatch_once_t sMDNSMulticastAddrV4InitOnce = 0;
17147
17148 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init);
17149 return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
17150 }
17151
17152 static void _MDNSMulticastAddrV4Init( void *inContext )
17153 {
17154 struct sockaddr_in * const addr = (struct sockaddr_in *) inContext;
17155
17156 memset( addr, 0, sizeof( *addr ) );
17157 SIN_LEN_SET( addr );
17158 addr->sin_family = AF_INET;
17159 addr->sin_port = htons( kMDNSPort );
17160 addr->sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
17161 }
17162
17163 //===========================================================================================================================
17164 // GetMDNSMulticastAddrV6
17165 //===========================================================================================================================
17166
17167 static void _MDNSMulticastAddrV6Init( void *inContext );
17168
17169 static const struct sockaddr * GetMDNSMulticastAddrV6( void )
17170 {
17171 static struct sockaddr_in6 sMDNSMulticastAddrV6;
17172 static dispatch_once_t sMDNSMulticastAddrV6InitOnce = 0;
17173
17174 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init);
17175 return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
17176 }
17177
17178 static void _MDNSMulticastAddrV6Init( void *inContext )
17179 {
17180 struct sockaddr_in6 * const addr = (struct sockaddr_in6 *) inContext;
17181
17182 memset( addr, 0, sizeof( *addr ) );
17183 SIN6_LEN_SET( addr );
17184 addr->sin6_family = AF_INET6;
17185 addr->sin6_port = htons( kMDNSPort );
17186 addr->sin6_addr.s6_addr[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
17187 addr->sin6_addr.s6_addr[ 1 ] = 0x02;
17188 addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
17189 }
17190
17191 //===========================================================================================================================
17192 // GetAnyMDNSInterface
17193 //===========================================================================================================================
17194
17195 static OSStatus GetAnyMDNSInterface( char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
17196 {
17197 OSStatus err;
17198 struct ifaddrs * ifaList;
17199 const struct ifaddrs * ifa;
17200 const struct ifaddrs * ifa2;
17201 const char * ifname = NULL;
17202 const unsigned int checkFlags = IFF_UP | IFF_MULTICAST | IFF_LOOPBACK | IFF_POINTOPOINT;
17203 const unsigned int wantFlags = IFF_UP | IFF_MULTICAST;
17204 int wantFamily;
17205 NetTransportType type;
17206
17207 ifaList = NULL;
17208 err = getifaddrs( &ifaList );
17209 err = map_global_noerr_errno( err );
17210 require_noerr( err, exit );
17211
17212 for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
17213 {
17214 if( ( ifa->ifa_flags & checkFlags ) != wantFlags ) continue;
17215 if( !ifa->ifa_addr || !ifa->ifa_name ) continue;
17216 if( ( ifa->ifa_addr->sa_family != AF_INET ) &&
17217 ( ifa->ifa_addr->sa_family != AF_INET6 ) ) continue;
17218
17219 err = SocketGetInterfaceInfo( kInvalidSocketRef, ifa->ifa_name, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
17220 check_noerr( err );
17221 if( err || ( type == kNetTransportType_AWDL ) ) continue;
17222
17223 if( !ifname ) ifname = ifa->ifa_name;
17224 wantFamily = ( ifa->ifa_addr->sa_family == AF_INET ) ? AF_INET6 : AF_INET;
17225
17226 for( ifa2 = ifa->ifa_next; ifa2; ifa2 = ifa2->ifa_next )
17227 {
17228 if( ( ifa2->ifa_flags & checkFlags ) != wantFlags ) continue;
17229 if( !ifa2->ifa_addr || !ifa2->ifa_name ) continue;
17230 if( ifa2->ifa_addr->sa_family != wantFamily ) continue;
17231 if( strcmp( ifa2->ifa_name, ifa->ifa_name ) == 0 ) break;
17232 }
17233 if( ifa2 )
17234 {
17235 ifname = ifa->ifa_name;
17236 break;
17237 }
17238 }
17239 require_action_quiet( ifname, exit, err = kNotFoundErr );
17240
17241 if( inNameBuf ) strlcpy( inNameBuf, ifname, IF_NAMESIZE + 1 );
17242 if( outIndex ) *outIndex = if_nametoindex( ifname );
17243
17244 exit:
17245 if( ifaList ) freeifaddrs( ifaList );
17246 return( err );
17247 }
17248
17249 //===========================================================================================================================
17250 // CreateMulticastSocket
17251 //===========================================================================================================================
17252
17253 static OSStatus
17254 CreateMulticastSocket(
17255 const struct sockaddr * inAddr,
17256 int inPort,
17257 const char * inIfName,
17258 uint32_t inIfIndex,
17259 Boolean inJoin,
17260 int * outPort,
17261 SocketRef * outSock )
17262 {
17263 OSStatus err;
17264 SocketRef sock = kInvalidSocketRef;
17265 const int family = inAddr->sa_family;
17266 int port;
17267
17268 require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
17269
17270 err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
17271 require_noerr_quiet( err, exit );
17272
17273 err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
17274 require_noerr_quiet( err, exit );
17275
17276 if( family == AF_INET )
17277 {
17278 err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
17279 err = map_socket_noerr_errno( sock, err );
17280 require_noerr_quiet( err, exit );
17281 }
17282 else
17283 {
17284 err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
17285 err = map_socket_noerr_errno( sock, err );
17286 require_noerr_quiet( err, exit );
17287 }
17288
17289 if( inJoin )
17290 {
17291 err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
17292 require_noerr_quiet( err, exit );
17293 }
17294
17295 if( outPort ) *outPort = port;
17296 *outSock = sock;
17297 sock = kInvalidSocketRef;
17298
17299 exit:
17300 ForgetSocket( &sock );
17301 return( err );
17302 }
17303
17304 //===========================================================================================================================
17305 // DecimalTextToUInt32
17306 //===========================================================================================================================
17307
17308 static OSStatus DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
17309 {
17310 OSStatus err;
17311 uint64_t value;
17312 const char * ptr = inSrc;
17313
17314 require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
17315
17316 value = (uint64_t)( *ptr++ - '0' );
17317 if( value == 0 )
17318 {
17319 if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
17320 {
17321 err = kMalformedErr;
17322 goto exit;
17323 }
17324 }
17325 else
17326 {
17327 while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
17328 {
17329 value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
17330 require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
17331 }
17332 }
17333
17334 *outValue = (uint32_t) value;
17335 if( outPtr ) *outPtr = ptr;
17336 err = kNoErr;
17337
17338 exit:
17339 return( err );
17340 }
17341
17342 //===========================================================================================================================
17343 // CheckIntegerArgument
17344 //===========================================================================================================================
17345
17346 static OSStatus CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
17347 {
17348 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
17349
17350 FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
17351 return( kRangeErr );
17352 }
17353
17354 //===========================================================================================================================
17355 // CheckDoubleArgument
17356 //===========================================================================================================================
17357
17358 static OSStatus CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
17359 {
17360 if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
17361
17362 FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
17363 return( kRangeErr );
17364 }
17365
17366 //===========================================================================================================================
17367 // CheckRootUser
17368 //===========================================================================================================================
17369
17370 static OSStatus CheckRootUser( void )
17371 {
17372 if( geteuid() == 0 ) return( kNoErr );
17373
17374 FPrintF( stderr, "error: This command must to be run as root.\n" );
17375 return( kPermissionErr );
17376 }
17377
17378 //===========================================================================================================================
17379 // SpawnCommand
17380 //
17381 // Note: Based on systemf() from CoreUtils framework.
17382 //===========================================================================================================================
17383
17384 extern char ** environ;
17385
17386 static OSStatus SpawnCommand( pid_t *outPID, const char *inFormat, ... )
17387 {
17388 OSStatus err;
17389 va_list args;
17390 char * command;
17391 char * argv[ 4 ];
17392 pid_t pid;
17393
17394 command = NULL;
17395 va_start( args, inFormat );
17396 VASPrintF( &command, inFormat, args );
17397 va_end( args );
17398 require_action( command, exit, err = kUnknownErr );
17399
17400 argv[ 0 ] = "/bin/sh";
17401 argv[ 1 ] = "-c";
17402 argv[ 2 ] = command;
17403 argv[ 3 ] = NULL;
17404 err = posix_spawn( &pid, argv[ 0 ], NULL, NULL, argv, environ );
17405 free( command );
17406 require_noerr_quiet( err, exit );
17407
17408 if( outPID ) *outPID = pid;
17409
17410 exit:
17411 return( err );
17412 }
17413
17414 //===========================================================================================================================
17415 // OutputPropertyList
17416 //===========================================================================================================================
17417
17418 static OSStatus
17419 OutputPropertyList(
17420 CFPropertyListRef inPList,
17421 OutputFormatType inType,
17422 Boolean inAppendNewline,
17423 const char * inOutputFilePath )
17424 {
17425 OSStatus err;
17426 CFDataRef results = NULL;
17427 FILE * file = NULL;
17428
17429 // Convert plist to a specific format.
17430
17431 switch( inType )
17432 {
17433 case kOutputFormatType_JSON:
17434 results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
17435 require_action( results, exit, err = kUnknownErr );
17436 break;
17437
17438 case kOutputFormatType_XML:
17439 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
17440 require_action( results, exit, err = kUnknownErr );
17441 break;
17442
17443 case kOutputFormatType_Binary:
17444 results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
17445 require_action( results, exit, err = kUnknownErr );
17446 break;
17447
17448 default:
17449 err = kTypeErr;
17450 goto exit;
17451 }
17452
17453 // Write formatted results to file or stdout.
17454
17455 if( inOutputFilePath )
17456 {
17457 file = fopen( inOutputFilePath, "wb" );
17458 err = map_global_value_errno( file, file );
17459 require_noerr( err, exit );
17460 }
17461 else
17462 {
17463 file = stdout;
17464 }
17465
17466 err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
17467 require_noerr_quiet( err, exit );
17468
17469 // Write a trailing newline for JSON-formatted results if requested.
17470
17471 if( ( inType == kOutputFormatType_JSON ) && inAppendNewline )
17472 {
17473 err = WriteANSIFile( file, "\n", 1 );
17474 require_noerr_quiet( err, exit );
17475 }
17476
17477 exit:
17478 if( file && ( file != stdout ) ) fclose( file );
17479 CFReleaseNullSafe( results );
17480 return( err );
17481 }
17482
17483 //===========================================================================================================================
17484 // DNSRecordFixedFieldsSet
17485 //===========================================================================================================================
17486
17487 static void
17488 DNSRecordFixedFieldsSet(
17489 DNSRecordFixedFields * inFields,
17490 uint16_t inType,
17491 uint16_t inClass,
17492 uint32_t inTTL,
17493 uint16_t inRDLength )
17494 {
17495 WriteBig16( inFields->type, inType );
17496 WriteBig16( inFields->class, inClass );
17497 WriteBig32( inFields->ttl, inTTL );
17498 WriteBig16( inFields->rdlength, inRDLength );
17499 }
17500
17501 //===========================================================================================================================
17502 // SRVRecordDataFixedFieldsGet
17503 //===========================================================================================================================
17504
17505 static void
17506 SRVRecordDataFixedFieldsGet(
17507 const SRVRecordDataFixedFields * inFields,
17508 unsigned int * outPriority,
17509 unsigned int * outWeight,
17510 unsigned int * outPort )
17511 {
17512 if( outPriority ) *outPriority = ReadBig16( inFields->priority );
17513 if( outWeight ) *outWeight = ReadBig16( inFields->weight );
17514 if( outPort ) *outPort = ReadBig16( inFields->port );
17515 }
17516
17517 //===========================================================================================================================
17518 // SRVRecordDataFixedFieldsSet
17519 //===========================================================================================================================
17520
17521 static void
17522 SRVRecordDataFixedFieldsSet(
17523 SRVRecordDataFixedFields * inFields,
17524 uint16_t inPriority,
17525 uint16_t inWeight,
17526 uint16_t inPort )
17527 {
17528 WriteBig16( inFields->priority, inPriority );
17529 WriteBig16( inFields->weight, inWeight );
17530 WriteBig16( inFields->port, inPort );
17531 }
17532
17533 //===========================================================================================================================
17534 // SOARecordDataFixedFieldsGet
17535 //===========================================================================================================================
17536
17537 static void
17538 SOARecordDataFixedFieldsGet(
17539 const SOARecordDataFixedFields * inFields,
17540 uint32_t * outSerial,
17541 uint32_t * outRefresh,
17542 uint32_t * outRetry,
17543 uint32_t * outExpire,
17544 uint32_t * outMinimum )
17545 {
17546 if( outSerial ) *outSerial = ReadBig32( inFields->serial );
17547 if( outRefresh ) *outRefresh = ReadBig32( inFields->refresh );
17548 if( outRetry ) *outRetry = ReadBig32( inFields->retry );
17549 if( outExpire ) *outExpire = ReadBig32( inFields->expire );
17550 if( outMinimum ) *outMinimum = ReadBig32( inFields->minimum );
17551 }
17552
17553 //===========================================================================================================================
17554 // SOARecordDataFixedFieldsSet
17555 //===========================================================================================================================
17556
17557 static void
17558 SOARecordDataFixedFieldsSet(
17559 SOARecordDataFixedFields * inFields,
17560 uint32_t inSerial,
17561 uint32_t inRefresh,
17562 uint32_t inRetry,
17563 uint32_t inExpire,
17564 uint32_t inMinimum )
17565 {
17566 WriteBig32( inFields->serial, inSerial );
17567 WriteBig32( inFields->refresh, inRefresh );
17568 WriteBig32( inFields->retry, inRetry );
17569 WriteBig32( inFields->expire, inExpire );
17570 WriteBig32( inFields->minimum, inMinimum );
17571 }
17572
17573 //===========================================================================================================================
17574 // CreateSRVRecordDataFromString
17575 //===========================================================================================================================
17576
17577 static OSStatus CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
17578 {
17579 OSStatus err;
17580 DataBuffer dataBuf;
17581 const char * ptr;
17582 int i;
17583 uint8_t * end;
17584 uint8_t target[ kDomainNameLengthMax ];
17585
17586 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
17587
17588 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
17589
17590 ptr = inString;
17591 for( i = 0; i < 3; ++i )
17592 {
17593 char * next;
17594 long value;
17595 uint8_t buf[ 2 ];
17596
17597 value = strtol( ptr, &next, 0 );
17598 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
17599 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
17600 ptr = next + 1;
17601
17602 WriteBig16( buf, value );
17603
17604 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
17605 require_noerr( err, exit );
17606 }
17607
17608 // Set the target domain name.
17609
17610 err = DomainNameFromString( target, ptr, &end );
17611 require_noerr_quiet( err, exit );
17612
17613 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
17614 require_noerr( err, exit );
17615
17616 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
17617 require_noerr( err, exit );
17618
17619 exit:
17620 DataBuffer_Free( &dataBuf );
17621 return( err );
17622 }
17623
17624 //===========================================================================================================================
17625 // CreateTXTRecordDataFromString
17626 //===========================================================================================================================
17627
17628 static OSStatus CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
17629 {
17630 OSStatus err;
17631 DataBuffer dataBuf;
17632 const char * src;
17633 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
17634
17635 DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
17636
17637 src = inString;
17638 for( ;; )
17639 {
17640 uint8_t * dst = &txtStr[ 1 ];
17641 const uint8_t * const lim = &txtStr[ 256 ];
17642 int c;
17643
17644 while( *src && ( *src != inDelimiter ) )
17645 {
17646 if( ( c = *src++ ) == '\\' )
17647 {
17648 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
17649 c = *src++;
17650 }
17651 require_action_quiet( dst < lim, exit, err = kOverrunErr );
17652 *dst++ = (uint8_t) c;
17653 }
17654 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
17655 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
17656 require_noerr( err, exit );
17657
17658 if( *src == '\0' ) break;
17659 ++src;
17660 }
17661
17662 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
17663 require_noerr( err, exit );
17664
17665 exit:
17666 DataBuffer_Free( &dataBuf );
17667 return( err );
17668 }
17669
17670 //===========================================================================================================================
17671 // CreateNSECRecordData
17672 //===========================================================================================================================
17673
17674 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
17675 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
17676
17677 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
17678
17679 static OSStatus
17680 CreateNSECRecordData(
17681 const uint8_t * inNextDomainName,
17682 uint8_t ** outPtr,
17683 size_t * outLen,
17684 unsigned int inTypeCount,
17685 ... )
17686 {
17687 OSStatus err;
17688 va_list args;
17689 DataBuffer rdataDB;
17690 unsigned int * array = NULL;
17691 unsigned int i, type, maxBit, currBlock, bitmapLen;
17692 uint8_t fields[ 2 + kNSECBitmapMaxLength ];
17693 uint8_t * const bitmap = &fields[ 2 ];
17694
17695 va_start( args, inTypeCount );
17696 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
17697
17698 // Append Next Domain Name.
17699
17700 err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
17701 require_noerr( err, exit );
17702
17703 // Append Type Bit Maps.
17704
17705 maxBit = 0;
17706 memset( bitmap, 0, kNSECBitmapMaxLength );
17707 if( inTypeCount > 0 )
17708 {
17709 array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
17710 require_action( array, exit, err = kNoMemoryErr );
17711
17712 for( i = 0; i < inTypeCount; ++i )
17713 {
17714 type = va_arg( args, unsigned int );
17715 require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
17716 array[ i ] = type;
17717 }
17718 qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
17719
17720 currBlock = array[ 0 ] / 256;
17721 for( i = 0; i < inTypeCount; ++i )
17722 {
17723 const unsigned int block = array[ i ] / 256;
17724 const unsigned int bit = array[ i ] % 256;
17725
17726 if( block != currBlock )
17727 {
17728 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
17729 fields[ 0 ] = (uint8_t) currBlock;
17730 fields[ 1 ] = (uint8_t) bitmapLen;
17731
17732 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
17733 require_noerr( err, exit );
17734
17735 maxBit = 0;
17736 currBlock = block;
17737 memset( bitmap, 0, bitmapLen );
17738 }
17739 BitArray_SetBit( bitmap, bit );
17740 if( bit > maxBit ) maxBit = bit;
17741 }
17742 }
17743 else
17744 {
17745 currBlock = 0;
17746 }
17747
17748 bitmapLen = BitArray_MaxBytes( maxBit + 1 );
17749 fields[ 0 ] = (uint8_t) currBlock;
17750 fields[ 1 ] = (uint8_t) bitmapLen;
17751
17752 err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
17753 require_noerr( err, exit );
17754
17755 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
17756 require_noerr( err, exit );
17757
17758 exit:
17759 va_end( args );
17760 DataBuffer_Free( &rdataDB );
17761 FreeNullSafe( array );
17762 return( err );
17763 }
17764
17765 //===========================================================================================================================
17766 // AppendSOARecord
17767 //===========================================================================================================================
17768
17769 static OSStatus
17770 _AppendSOARecordData(
17771 DataBuffer * inDB,
17772 const uint8_t * inMName,
17773 const uint8_t * inRName,
17774 uint32_t inSerial,
17775 uint32_t inRefresh,
17776 uint32_t inRetry,
17777 uint32_t inExpire,
17778 uint32_t inMinimumTTL,
17779 size_t * outLen );
17780
17781 static OSStatus
17782 AppendSOARecord(
17783 DataBuffer * inDB,
17784 const uint8_t * inNamePtr,
17785 size_t inNameLen,
17786 uint16_t inType,
17787 uint16_t inClass,
17788 uint32_t inTTL,
17789 const uint8_t * inMName,
17790 const uint8_t * inRName,
17791 uint32_t inSerial,
17792 uint32_t inRefresh,
17793 uint32_t inRetry,
17794 uint32_t inExpire,
17795 uint32_t inMinimumTTL,
17796 size_t * outLen )
17797 {
17798 OSStatus err;
17799 size_t rdataLen;
17800 size_t rdlengthOffset = 0;
17801 uint8_t * rdlengthPtr;
17802
17803 if( inDB )
17804 {
17805 err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
17806 require_noerr( err, exit );
17807
17808 rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
17809 }
17810
17811 err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
17812 require_noerr( err, exit );
17813
17814 if( inDB )
17815 {
17816 rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
17817 WriteBig16( rdlengthPtr, rdataLen );
17818 }
17819
17820 if( outLen ) *outLen = inNameLen + sizeof( DNSRecordFixedFields ) + rdataLen;
17821 err = kNoErr;
17822
17823 exit:
17824 return( err );
17825 }
17826
17827 static OSStatus
17828 _AppendSOARecordData(
17829 DataBuffer * inDB,
17830 const uint8_t * inMName,
17831 const uint8_t * inRName,
17832 uint32_t inSerial,
17833 uint32_t inRefresh,
17834 uint32_t inRetry,
17835 uint32_t inExpire,
17836 uint32_t inMinimumTTL,
17837 size_t * outLen )
17838 {
17839 OSStatus err;
17840 SOARecordDataFixedFields fields;
17841 const size_t mnameLen = DomainNameLength( inMName );
17842 const size_t rnameLen = DomainNameLength( inRName );
17843
17844 if( inDB )
17845 {
17846 err = DataBuffer_Append( inDB, inMName, mnameLen );
17847 require_noerr( err, exit );
17848
17849 err = DataBuffer_Append( inDB, inRName, rnameLen );
17850 require_noerr( err, exit );
17851
17852 SOARecordDataFixedFieldsSet( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
17853 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
17854 require_noerr( err, exit );
17855 }
17856 if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
17857 err = kNoErr;
17858
17859 exit:
17860 return( err );
17861 }
17862
17863 //===========================================================================================================================
17864 // CreateSOARecordData
17865 //===========================================================================================================================
17866
17867 static OSStatus
17868 CreateSOARecordData(
17869 const uint8_t * inMName,
17870 const uint8_t * inRName,
17871 uint32_t inSerial,
17872 uint32_t inRefresh,
17873 uint32_t inRetry,
17874 uint32_t inExpire,
17875 uint32_t inMinimumTTL,
17876 uint8_t ** outPtr,
17877 size_t * outLen )
17878 {
17879 OSStatus err;
17880 DataBuffer rdataDB;
17881
17882 DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
17883
17884 err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
17885 require_noerr( err, exit );
17886
17887 err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
17888 require_noerr( err, exit );
17889
17890 exit:
17891 DataBuffer_Free( &rdataDB );
17892 return( err );
17893 }
17894
17895 //===========================================================================================================================
17896 // _DataBuffer_AppendDNSQuestion
17897 //===========================================================================================================================
17898
17899 static OSStatus
17900 _DataBuffer_AppendDNSQuestion(
17901 DataBuffer * inDB,
17902 const uint8_t * inNamePtr,
17903 size_t inNameLen,
17904 uint16_t inType,
17905 uint16_t inClass )
17906 {
17907 OSStatus err;
17908 DNSQuestionFixedFields fields;
17909
17910 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
17911 require_noerr( err, exit );
17912
17913 DNSQuestionFixedFieldsInit( &fields, inType, inClass );
17914 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
17915 require_noerr( err, exit );
17916
17917 exit:
17918 return( err );
17919 }
17920
17921 //===========================================================================================================================
17922 // _DataBuffer_AppendDNSRecord
17923 //===========================================================================================================================
17924
17925 static OSStatus
17926 _DataBuffer_AppendDNSRecord(
17927 DataBuffer * inDB,
17928 const uint8_t * inNamePtr,
17929 size_t inNameLen,
17930 uint16_t inType,
17931 uint16_t inClass,
17932 uint32_t inTTL,
17933 const uint8_t * inRDataPtr,
17934 size_t inRDataLen )
17935 {
17936 OSStatus err;
17937 DNSRecordFixedFields fields;
17938
17939 require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
17940
17941 err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
17942 require_noerr( err, exit );
17943
17944 DNSRecordFixedFieldsSet( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
17945 err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
17946 require_noerr( err, exit );
17947
17948 if( inRDataPtr )
17949 {
17950 err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
17951 require_noerr( err, exit );
17952 }
17953
17954 exit:
17955 return( err );
17956 }
17957
17958 //===========================================================================================================================
17959 // _NanoTime64ToDateString
17960 //===========================================================================================================================
17961
17962 static char * _NanoTime64ToDateString( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
17963 {
17964 struct timeval tv;
17965
17966 NanoTimeToTimeVal( inTime, &tv );
17967 return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
17968 }
17969
17970 //===========================================================================================================================
17971 // MDNSColliderCreate
17972 //===========================================================================================================================
17973
17974 typedef enum
17975 {
17976 kMDNSColliderOpCode_Invalid = 0,
17977 kMDNSColliderOpCode_Send = 1,
17978 kMDNSColliderOpCode_Wait = 2,
17979 kMDNSColliderOpCode_SetProbeActions = 3,
17980 kMDNSColliderOpCode_LoopPush = 4,
17981 kMDNSColliderOpCode_LoopPop = 5,
17982 kMDNSColliderOpCode_Exit = 6
17983
17984 } MDNSColliderOpCode;
17985
17986 typedef struct
17987 {
17988 MDNSColliderOpCode opcode;
17989 uint32_t operand;
17990
17991 } MDNSCInstruction;
17992
17993 #define kMaxLoopDepth 16
17994
17995 struct MDNSColliderPrivate
17996 {
17997 CFRuntimeBase base; // CF object base.
17998 dispatch_queue_t queue; // Queue for collider's events.
17999 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
18000 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
18001 SocketRef sockV4; // IPv4 UDP socket for mDNS.
18002 SocketRef sockV6; // IPv6 UDP socket for mDNS.
18003 uint8_t * target; // Record name being targeted. (malloced)
18004 uint8_t * responsePtr; // Response message pointer. (malloced)
18005 size_t responseLen; // Response message length.
18006 uint8_t * probePtr; // Probe query message pointer. (malloced)
18007 size_t probeLen; // Probe query message length.
18008 unsigned int probeCount; // Count of probe queries received for collider's record.
18009 uint32_t probeActionMap; // Bitmap of actions to take for
18010 MDNSCInstruction * program; // Program to execute.
18011 uint32_t pc; // Program's program counter.
18012 uint32_t loopCounts[ kMaxLoopDepth ]; // Stack of loop counters.
18013 uint32_t loopDepth; // Current loop depth.
18014 dispatch_source_t waitTimer; // Timer for program's wait commands.
18015 uint32_t interfaceIndex; // Interface over which to send and receive mDNS msgs.
18016 MDNSColliderStopHandler_f stopHandler; // User's stop handler.
18017 void * stopContext; // User's stop handler context.
18018 MDNSColliderProtocols protocols; // Protocols to use, i.e., IPv4, IPv6.
18019 Boolean stopped; // True if the collider has been stopped.
18020 uint8_t msgBuf[ kMDNSMessageSizeMax ]; // mDNS message buffer.
18021 };
18022
18023 static void _MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
18024 static void _MDNSColliderReadHandler( void *inContext );
18025 static void _MDNSColliderExecuteProgram( void *inContext );
18026 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
18027 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
18028
18029 CF_CLASS_DEFINE( MDNSCollider );
18030
18031 ulog_define_ex( "com.apple.dnssdutil", MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
18032 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
18033
18034 static OSStatus MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
18035 {
18036 OSStatus err;
18037 MDNSColliderRef obj = NULL;
18038
18039 CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
18040
18041 ReplaceDispatchQueue( &obj->queue, inQueue );
18042 obj->sockV4 = kInvalidSocketRef;
18043 obj->sockV6 = kInvalidSocketRef;
18044
18045 *outCollider = obj;
18046 err = kNoErr;
18047
18048 exit:
18049 return( err );
18050 }
18051
18052 //===========================================================================================================================
18053 // _MDNSColliderFinalize
18054 //===========================================================================================================================
18055
18056 static void _MDNSColliderFinalize( CFTypeRef inObj )
18057 {
18058 MDNSColliderRef const me = (MDNSColliderRef) inObj;
18059
18060 check( !me->waitTimer );
18061 check( !me->readSourceV4 );
18062 check( !me->readSourceV6 );
18063 check( !IsValidSocket( me->sockV4 ) );
18064 check( !IsValidSocket( me->sockV6 ) );
18065 ForgetMem( &me->target );
18066 ForgetMem( &me->responsePtr );
18067 ForgetMem( &me->probePtr );
18068 ForgetMem( &me->program );
18069 dispatch_forget( &me->queue );
18070 }
18071
18072 //===========================================================================================================================
18073 // MDNSColliderStart
18074 //===========================================================================================================================
18075
18076 static void _MDNSColliderStart( void *inContext );
18077
18078 static OSStatus MDNSColliderStart( MDNSColliderRef me )
18079 {
18080 OSStatus err;
18081
18082 require_action_quiet( me->target, exit, err = kNotPreparedErr );
18083 require_action_quiet( me->responsePtr, exit, err = kNotPreparedErr );
18084 require_action_quiet( me->probePtr, exit, err = kNotPreparedErr );
18085 require_action_quiet( me->program, exit, err = kNotPreparedErr );
18086 require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
18087 require_action_quiet( me->protocols, exit, err = kNotPreparedErr );
18088
18089 CFRetain( me );
18090 dispatch_async_f( me->queue, me, _MDNSColliderStart );
18091 err = kNoErr;
18092
18093 exit:
18094 return( err );
18095 }
18096
18097 static void _MDNSColliderStart( void *inContext )
18098 {
18099 OSStatus err;
18100 MDNSColliderRef const me = (MDNSColliderRef) inContext;
18101 SocketRef sock = kInvalidSocketRef;
18102 SocketContext * sockCtx = NULL;
18103
18104 if( me->protocols & kMDNSColliderProtocol_IPv4 )
18105 {
18106 err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
18107 require_noerr( err, exit );
18108
18109 err = SocketContextCreate( sock, me, &sockCtx );
18110 require_noerr( err, exit );
18111 sock = kInvalidSocketRef;
18112
18113 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
18114 sockCtx, &me->readSourceV4 );
18115 require_noerr( err, exit );
18116 me->sockV4 = sockCtx->sock;
18117 sockCtx = NULL;
18118
18119 dispatch_resume( me->readSourceV4 );
18120 }
18121
18122 if( me->protocols & kMDNSColliderProtocol_IPv6 )
18123 {
18124 err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
18125 require_noerr( err, exit );
18126
18127 err = SocketContextCreate( sock, me, &sockCtx );
18128 require_noerr( err, exit );
18129 sock = kInvalidSocketRef;
18130
18131 err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
18132 sockCtx, &me->readSourceV6 );
18133 require_noerr( err, exit );
18134 me->sockV6 = sockCtx->sock;
18135 sockCtx = NULL;
18136
18137 dispatch_resume( me->readSourceV6 );
18138 }
18139
18140 _MDNSColliderExecuteProgram( me );
18141 err = kNoErr;
18142
18143 exit:
18144 ForgetSocket( &sock );
18145 ForgetSocketContext( &sockCtx );
18146 if( err ) _MDNSColliderStop( me, err );
18147 }
18148
18149 //===========================================================================================================================
18150 // MDNSColliderStop
18151 //===========================================================================================================================
18152
18153 static void _MDNSColliderUserStop( void *inContext );
18154
18155 static void MDNSColliderStop( MDNSColliderRef me )
18156 {
18157 CFRetain( me );
18158 dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
18159 }
18160
18161 static void _MDNSColliderUserStop( void *inContext )
18162 {
18163 MDNSColliderRef const me = (MDNSColliderRef) inContext;
18164
18165 _MDNSColliderStop( me, kCanceledErr );
18166 CFRelease( me );
18167 }
18168
18169 //===========================================================================================================================
18170 // MDNSColliderSetProtocols
18171 //===========================================================================================================================
18172
18173 static void MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
18174 {
18175 me->protocols = inProtocols;
18176 }
18177
18178 //===========================================================================================================================
18179 // MDNSColliderSetInterfaceIndex
18180 //===========================================================================================================================
18181
18182 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
18183 {
18184 me->interfaceIndex = inInterfaceIndex;
18185 }
18186
18187 //===========================================================================================================================
18188 // MDNSColliderSetProgram
18189 //===========================================================================================================================
18190
18191 #define kMDNSColliderProgCmd_Done "done"
18192 #define kMDNSColliderProgCmd_Loop "loop"
18193 #define kMDNSColliderProgCmd_Send "send"
18194 #define kMDNSColliderProgCmd_Probes "probes"
18195 #define kMDNSColliderProgCmd_Wait "wait"
18196
18197 typedef uint32_t MDNSColliderProbeAction;
18198
18199 #define kMDNSColliderProbeAction_None 0
18200 #define kMDNSColliderProbeAction_Respond 1
18201 #define kMDNSColliderProbeAction_RespondUnicast 2
18202 #define kMDNSColliderProbeAction_RespondMulticast 3
18203 #define kMDNSColliderProbeAction_Probe 4
18204 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
18205
18206 #define kMDNSColliderProbeActionBits_Count 3
18207 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
18208 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
18209
18210 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
18211
18212 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
18213
18214 static OSStatus MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
18215 {
18216 OSStatus err;
18217 uint32_t insCount;
18218 unsigned int loopDepth;
18219 const char * cmd;
18220 const char * end;
18221 const char * next;
18222 MDNSCInstruction * program = NULL;
18223 uint32_t loopStart[ kMaxLoopDepth ];
18224
18225 insCount = 0;
18226 for( cmd = inProgramStr; *cmd; cmd = next )
18227 {
18228 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
18229 require_action_quiet( end != cmd, exit, err = kMalformedErr );
18230 next = ( *end == ';' ) ? ( end + 1 ) : end;
18231 ++insCount;
18232 }
18233
18234 program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
18235 require_action( program, exit, err = kNoMemoryErr );
18236
18237 insCount = 0;
18238 loopDepth = 0;
18239 for( cmd = inProgramStr; *cmd; cmd = next )
18240 {
18241 size_t cmdLen;
18242 const char * ptr;
18243 const char * arg;
18244 size_t argLen;
18245 uint32_t value;
18246 MDNSCInstruction * const ins = &program[ insCount ];
18247
18248 while( isspace_safe( *cmd ) ) ++cmd;
18249 for( end = cmd; *end && ( *end != ';' ); ++end ) {}
18250 next = ( *end == ';' ) ? ( end + 1 ) : end;
18251
18252 for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
18253 cmdLen = (size_t)( ptr - cmd );
18254
18255 // Done statement
18256
18257 if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
18258 {
18259 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
18260 require_action_quiet( ptr == end, exit, err = kMalformedErr );
18261
18262 require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
18263
18264 ins->opcode = kMDNSColliderOpCode_LoopPop;
18265 ins->operand = loopStart[ --loopDepth ];
18266 }
18267
18268 // Loop command
18269
18270 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
18271 {
18272 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
18273 err = DecimalTextToUInt32( arg, end, &value, &ptr );
18274 require_noerr_quiet( err, exit );
18275 require_action_quiet( value > 0, exit, err = kValueErr );
18276
18277 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
18278 require_action_quiet( ptr == end, exit, err = kMalformedErr );
18279
18280 ins->opcode = kMDNSColliderOpCode_LoopPush;
18281 ins->operand = value;
18282
18283 require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
18284 loopStart[ loopDepth++ ] = insCount + 1;
18285 }
18286
18287 // Probes command
18288
18289 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
18290 {
18291 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
18292 for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
18293 argLen = (size_t)( ptr - arg );
18294 if( argLen > 0 )
18295 {
18296 err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
18297 require_noerr_quiet( err, exit );
18298 }
18299 else
18300 {
18301 value = 0;
18302 }
18303
18304 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
18305 require_action_quiet( ptr == end, exit, err = kMalformedErr );
18306
18307 ins->opcode = kMDNSColliderOpCode_SetProbeActions;
18308 ins->operand = value;
18309 }
18310
18311 // Send command
18312
18313 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
18314 {
18315 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
18316 require_action_quiet( ptr == end, exit, err = kMalformedErr );
18317
18318 ins->opcode = kMDNSColliderOpCode_Send;
18319 }
18320
18321 // Wait command
18322
18323 else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
18324 {
18325 for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
18326 err = DecimalTextToUInt32( arg, end, &value, &ptr );
18327 require_noerr_quiet( err, exit );
18328
18329 while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
18330 require_action_quiet( ptr == end, exit, err = kMalformedErr );
18331
18332 ins->opcode = kMDNSColliderOpCode_Wait;
18333 ins->operand = value;
18334 }
18335
18336 // Unrecognized command
18337
18338 else
18339 {
18340 err = kCommandErr;
18341 goto exit;
18342 }
18343 ++insCount;
18344 }
18345 require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
18346
18347 program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
18348
18349 FreeNullSafe( me->program );
18350 me->program = program;
18351 program = NULL;
18352 err = kNoErr;
18353
18354 exit:
18355 FreeNullSafe( program );
18356 return( err );
18357 }
18358
18359 static OSStatus _MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
18360 {
18361 OSStatus err;
18362 const char * ptr;
18363 const char * const end = &inString[ inLen ];
18364 uint32_t bitmap;
18365 int index;
18366
18367 bitmap = 0;
18368 index = 0;
18369 ptr = inString;
18370 while( ptr < end )
18371 {
18372 int c, count;
18373 MDNSColliderProbeAction action;
18374
18375 c = *ptr++;
18376 if( isdigit_safe( c ) )
18377 {
18378 count = 0;
18379 do
18380 {
18381 count = ( count * 10 ) + ( c - '0' );
18382 require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
18383 require_action_quiet( ptr < end, exit, err = kUnderrunErr );
18384 c = *ptr++;
18385
18386 } while( isdigit_safe( c ) );
18387 require_action_quiet( count > 0, exit, err = kCountErr );
18388 }
18389 else
18390 {
18391 require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
18392 count = 1;
18393 }
18394
18395 switch( c )
18396 {
18397 case 'n': action = kMDNSColliderProbeAction_None; break;
18398 case 'r': action = kMDNSColliderProbeAction_Respond; break;
18399 case 'u': action = kMDNSColliderProbeAction_RespondUnicast; break;
18400 case 'm': action = kMDNSColliderProbeAction_RespondMulticast; break;
18401 case 'p': action = kMDNSColliderProbeAction_Probe; break;
18402 default: err = kMalformedErr; goto exit;
18403 }
18404 if( ptr < end )
18405 {
18406 c = *ptr++;
18407 require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
18408 }
18409 while( count-- > 0 )
18410 {
18411 bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
18412 ++index;
18413 }
18414 }
18415
18416 *outBitmap = bitmap;
18417 err = kNoErr;
18418
18419 exit:
18420 return( err );
18421 }
18422
18423 //===========================================================================================================================
18424 // MDNSColliderSetStopHandler
18425 //===========================================================================================================================
18426
18427 static void MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
18428 {
18429 me->stopHandler = inStopHandler;
18430 me->stopContext = inStopContext;
18431 }
18432
18433 //===========================================================================================================================
18434 // MDNSColliderSetRecord
18435 //===========================================================================================================================
18436
18437 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" kLocalStr
18438 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
18439 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
18440
18441 static OSStatus
18442 MDNSColliderSetRecord(
18443 MDNSColliderRef me,
18444 const uint8_t * inName,
18445 uint16_t inType,
18446 const void * inRDataPtr,
18447 size_t inRDataLen )
18448 {
18449 OSStatus err;
18450 DataBuffer msgDB;
18451 DNSHeader header;
18452 uint8_t * targetPtr = NULL;
18453 size_t targetLen;
18454 uint8_t * responsePtr = NULL;
18455 size_t responseLen;
18456 uint8_t * probePtr = NULL;
18457 size_t probeLen;
18458
18459 DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
18460
18461 err = DomainNameDup( inName, &targetPtr, &targetLen );
18462 require_noerr_quiet( err, exit );
18463
18464 // Create response message.
18465
18466 memset( &header, 0, sizeof( header ) );
18467 DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
18468 DNSHeaderSetAnswerCount( &header, 1 );
18469
18470 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
18471 require_noerr( err, exit );
18472
18473 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kRRClassCacheFlushBit,
18474 1976, inRDataPtr, inRDataLen );
18475 require_noerr( err, exit );
18476
18477 err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
18478 require_noerr( err, exit );
18479
18480 // Create probe message.
18481
18482 memset( &header, 0, sizeof( header ) );
18483 DNSHeaderSetQuestionCount( &header, 2 );
18484 DNSHeaderSetAuthorityCount( &header, 1 );
18485
18486 err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
18487 require_noerr( err, exit );
18488
18489 err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
18490 require_noerr( err, exit );
18491
18492 err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
18493 kDNSServiceType_NULL, kDNSServiceClass_IN );
18494 require_noerr( err, exit );
18495
18496 err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
18497 1976, inRDataPtr, inRDataLen );
18498 require_noerr( err, exit );
18499
18500 err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
18501 require_noerr( err, exit );
18502
18503 FreeNullSafe( me->target );
18504 me->target = targetPtr;
18505 targetPtr = NULL;
18506
18507 FreeNullSafe( me->responsePtr );
18508 me->responsePtr = responsePtr;
18509 me->responseLen = responseLen;
18510 responsePtr = NULL;
18511
18512 FreeNullSafe( me->probePtr );
18513 me->probePtr = probePtr;
18514 me->probeLen = probeLen;
18515 probePtr = NULL;
18516
18517 exit:
18518 DataBuffer_Free( &msgDB );
18519 FreeNullSafe( targetPtr );
18520 FreeNullSafe( responsePtr );
18521 FreeNullSafe( probePtr );
18522 return( err );
18523 }
18524
18525 //===========================================================================================================================
18526 // _MDNSColliderStop
18527 //===========================================================================================================================
18528
18529 static void _MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
18530 {
18531 dispatch_source_forget( &me->waitTimer );
18532 dispatch_source_forget( &me->readSourceV4 );
18533 dispatch_source_forget( &me->readSourceV6 );
18534 me->sockV4 = kInvalidSocketRef;
18535 me->sockV6 = kInvalidSocketRef;
18536
18537 if( !me->stopped )
18538 {
18539 me->stopped = true;
18540 if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
18541 CFRelease( me );
18542 }
18543 }
18544
18545 //===========================================================================================================================
18546 // _MDNSColliderReadHandler
18547 //===========================================================================================================================
18548
18549 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
18550 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
18551
18552 static void _MDNSColliderReadHandler( void *inContext )
18553 {
18554 OSStatus err;
18555 struct timeval now;
18556 SocketContext * const sockCtx = (SocketContext *) inContext;
18557 MDNSColliderRef const me = (MDNSColliderRef) sockCtx->userContext;
18558 size_t msgLen;
18559 sockaddr_ip sender;
18560 const DNSHeader * hdr;
18561 const uint8_t * ptr;
18562 const struct sockaddr * dest;
18563 int probeFound, probeIsQU;
18564 unsigned int qCount, i;
18565 MDNSColliderProbeAction action;
18566
18567 gettimeofday( &now, NULL );
18568
18569 err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
18570 NULL, NULL, NULL, NULL );
18571 require_noerr( err, exit );
18572
18573 require_quiet( msgLen >= kDNSHeaderLength, exit );
18574 hdr = (const DNSHeader *) me->msgBuf;
18575
18576 probeFound = false;
18577 probeIsQU = false;
18578 qCount = DNSHeaderGetQuestionCount( hdr );
18579 ptr = (const uint8_t *) &hdr[ 1 ];
18580 for( i = 0; i < qCount; ++i )
18581 {
18582 uint16_t qtype, qclass;
18583 uint8_t qname[ kDomainNameLengthMax ];
18584
18585 err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
18586 require_noerr_quiet( err, exit );
18587
18588 if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
18589 DomainNameEqual( qname, kMDNSColliderDummyName ) )
18590 {
18591 probeFound = false;
18592 break;
18593 }
18594
18595 if( qtype != kDNSServiceType_ANY ) continue;
18596 if( ( qclass & ~kQClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
18597 if( !DomainNameEqual( qname, me->target ) ) continue;
18598
18599 if( !probeFound )
18600 {
18601 probeFound = true;
18602 probeIsQU = ( qclass & kQClassUnicastResponseBit ) ? true : false;
18603 }
18604 }
18605 require_quiet( probeFound, exit );
18606
18607 ++me->probeCount;
18608 action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
18609
18610 mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
18611 &sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
18612
18613 if( ( action == kMDNSColliderProbeAction_Respond ) ||
18614 ( action == kMDNSColliderProbeAction_RespondUnicast ) ||
18615 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
18616 {
18617 if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
18618 ( action == kMDNSColliderProbeAction_RespondUnicast ) )
18619 {
18620 dest = &sender.sa;
18621 }
18622 else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
18623 ( action == kMDNSColliderProbeAction_RespondMulticast ) )
18624 {
18625 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
18626 }
18627
18628 err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
18629 require_noerr( err, exit );
18630 }
18631 else if( action == kMDNSColliderProbeAction_Probe )
18632 {
18633 dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
18634
18635 err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
18636 require_noerr( err, exit );
18637 }
18638
18639 exit:
18640 return;
18641 }
18642
18643 static MDNSColliderProbeAction _MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
18644 {
18645 MDNSColliderProbeAction action;
18646
18647 if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
18648 {
18649 action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
18650 kMDNSColliderProbeActionBits_Mask;
18651 }
18652 else
18653 {
18654 action = kMDNSColliderProbeAction_None;
18655 }
18656 return( action );
18657 }
18658
18659 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
18660 {
18661 switch( inAction )
18662 {
18663 case kMDNSColliderProbeAction_None: return( "None" );
18664 case kMDNSColliderProbeAction_Respond: return( "Respond" );
18665 case kMDNSColliderProbeAction_RespondUnicast: return( "Respond (unicast)" );
18666 case kMDNSColliderProbeAction_RespondMulticast: return( "Respond (multicast)" );
18667 case kMDNSColliderProbeAction_Probe: return( "Probe" );
18668 default: return( "???" );
18669 }
18670 }
18671
18672 //===========================================================================================================================
18673 // _MDNSColliderExecuteProgram
18674 //===========================================================================================================================
18675
18676 static void _MDNSColliderExecuteProgram( void *inContext )
18677 {
18678 OSStatus err;
18679 MDNSColliderRef const me = (MDNSColliderRef) inContext;
18680 int stop;
18681
18682 dispatch_forget( &me->waitTimer );
18683
18684 stop = false;
18685 for( ;; )
18686 {
18687 const MDNSCInstruction * const ins = &me->program[ me->pc++ ];
18688 uint32_t waitMs;
18689
18690 switch( ins->opcode )
18691 {
18692 case kMDNSColliderOpCode_Send:
18693 if( IsValidSocket( me->sockV4 ) )
18694 {
18695 err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
18696 require_noerr( err, exit );
18697 }
18698 if( IsValidSocket( me->sockV6 ) )
18699 {
18700 err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
18701 require_noerr( err, exit );
18702 }
18703 break;
18704
18705 case kMDNSColliderOpCode_Wait:
18706 waitMs = ins->operand;
18707 if( waitMs > 0 )
18708 {
18709 err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
18710 _MDNSColliderExecuteProgram, me, &me->waitTimer );
18711 require_noerr( err, exit );
18712 dispatch_resume( me->waitTimer );
18713 goto exit;
18714 }
18715 break;
18716
18717 case kMDNSColliderOpCode_SetProbeActions:
18718 me->probeCount = 0;
18719 me->probeActionMap = ins->operand;
18720 break;
18721
18722 case kMDNSColliderOpCode_LoopPush:
18723 check( me->loopDepth < kMaxLoopDepth );
18724 me->loopCounts[ me->loopDepth++ ] = ins->operand;
18725 break;
18726
18727 case kMDNSColliderOpCode_LoopPop:
18728 check( me->loopDepth > 0 );
18729 if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
18730 {
18731 me->pc = ins->operand;
18732 }
18733 else
18734 {
18735 --me->loopDepth;
18736 }
18737 break;
18738
18739 case kMDNSColliderOpCode_Exit:
18740 stop = true;
18741 err = kNoErr;
18742 goto exit;
18743
18744 default:
18745 dlogassert( "Unhandled opcode %u\n", ins->opcode );
18746 err = kCommandErr;
18747 goto exit;
18748 }
18749 }
18750
18751 exit:
18752 if( err || stop ) _MDNSColliderStop( me, err );
18753 }
18754
18755 //===========================================================================================================================
18756 // _MDNSColliderSendResponse
18757 //===========================================================================================================================
18758
18759 static OSStatus _MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
18760 {
18761 OSStatus err;
18762 ssize_t n;
18763
18764 n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
18765 err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
18766 return( err );
18767 }
18768
18769 //===========================================================================================================================
18770 // _MDNSColliderSendProbe
18771 //===========================================================================================================================
18772
18773 static OSStatus _MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
18774 {
18775 OSStatus err;
18776 ssize_t n;
18777
18778 n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
18779 err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
18780 return( err );
18781 }
18782
18783 //===========================================================================================================================
18784 // ServiceBrowserCreate
18785 //===========================================================================================================================
18786
18787 typedef struct SBDomain SBDomain;
18788 typedef struct SBServiceType SBServiceType;
18789 typedef struct SBServiceBrowse SBServiceBrowse;
18790 typedef struct SBServiceInstance SBServiceInstance;
18791 typedef struct SBIPAddress SBIPAddress;
18792
18793 struct ServiceBrowserPrivate
18794 {
18795 CFRuntimeBase base; // CF object base.
18796 dispatch_queue_t queue; // Queue for service browser's events.
18797 DNSServiceRef connection; // Shared connection for DNS-SD ops.
18798 DNSServiceRef domainsQuery; // Query for recommended browsing domains.
18799 char * domain; // If non-null, then browsing is limited to this domain.
18800 StringListItem * serviceTypeList; // If non-null, then browsing is limited to these service types.
18801 ServiceBrowserCallback_f userCallback; // User's callback. Called when browsing stops.
18802 void * userContext; // User's callback context.
18803 SBDomain * domainList; // List of domains and their browse results.
18804 dispatch_source_t stopTimer; // Timer to stop browsing after browseTimeSecs.
18805 uint32_t ifIndex; // If non-zero, then browsing is limited to this interface.
18806 unsigned int browseTimeSecs; // Amount of time to spend browsing in seconds.
18807 Boolean includeAWDL; // True if the IncludeAWDL flag should be used for DNS-SD ops that
18808 // use the "any" interface.
18809 };
18810
18811 struct SBDomain
18812 {
18813 SBDomain * next; // Next domain object in list.
18814 ServiceBrowserRef browser; // Pointer to parent service browser.
18815 char * name; // Name of the domain.
18816 DNSServiceRef servicesQuery; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
18817 SBServiceType * typeList; // List of service types to browse for in this domain.
18818 };
18819
18820 struct SBServiceType
18821 {
18822 SBServiceType * next; // Next service type object in list.
18823 char * name; // Name of the service type.
18824 SBServiceBrowse * browseList; // List of browses for this service type.
18825 };
18826
18827 struct SBServiceBrowse
18828 {
18829 SBServiceBrowse * next; // Next browse object in list.
18830 ServiceBrowserRef browser; // Pointer to parent service browser.
18831 DNSServiceRef browse; // Reference to DNSServiceBrowse op.
18832 SBServiceInstance * instanceList; // List of service instances that were discovered by this browse.
18833 uint64_t startTicks; // Value of UpTicks() when the browse op began.
18834 uint32_t ifIndex; // If non-zero, then the browse is limited to this interface.
18835 };
18836
18837 struct SBServiceInstance
18838 {
18839 SBServiceInstance * next; // Next service instance object in list.
18840 ServiceBrowserRef browser; // Pointer to parent service browser.
18841 char * name; // Name of the service instance.
18842 uint32_t ifIndex; // Index of interface over which this service instance was discovered.
18843 uint64_t discoverTimeUs; // Time it took to discover this service instance in microseconds.
18844 DNSServiceRef resolve; // Reference to DNSServiceResolve op for this service instance.
18845 uint64_t resolveStartTicks; // Value of UpTicks() when the DNSServiceResolve op began.
18846 uint64_t resolveTimeUs; // Time it took to resolve this service instance.
18847 char * hostname; // Service instance's hostname. Result of DNSServiceResolve.
18848 uint16_t port; // Service instance's port number. Result of DNSServiceResolve.
18849 uint8_t * txtPtr; // Service instance's TXT record data. Result of DNSServiceResolve.
18850 size_t txtLen; // Length of service instance's TXT record data.
18851 DNSServiceRef getAddrInfo; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
18852 uint64_t gaiStartTicks; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
18853 SBIPAddress * ipaddrList; // List of IP addresses that the hostname resolved to.
18854 };
18855
18856 struct SBIPAddress
18857 {
18858 SBIPAddress * next; // Next IP address object in list.
18859 sockaddr_ip sip; // IPv4 or IPv6 address.
18860 uint64_t resolveTimeUs; // Time it took to resolve this IP address in microseconds.
18861 };
18862
18863 typedef struct
18864 {
18865 SBRDomain * domainList; // List of domains in which services were found.
18866 int32_t refCount; // This object's reference count.
18867
18868 } ServiceBrowserResultsPrivate;
18869
18870 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
18871 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
18872 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
18873 static void _ServiceBrowserTimerHandler( void *inContext );
18874 static void DNSSD_API
18875 _ServiceBrowserDomainsQueryCallback(
18876 DNSServiceRef inSDRef,
18877 DNSServiceFlags inFlags,
18878 uint32_t inInterfaceIndex,
18879 DNSServiceErrorType inError,
18880 const char * inFullName,
18881 uint16_t inType,
18882 uint16_t inClass,
18883 uint16_t inRDataLen,
18884 const void * inRDataPtr,
18885 uint32_t inTTL,
18886 void * inContext );
18887 static void DNSSD_API
18888 _ServiceBrowserServicesQueryCallback(
18889 DNSServiceRef inSDRef,
18890 DNSServiceFlags inFlags,
18891 uint32_t inInterfaceIndex,
18892 DNSServiceErrorType inError,
18893 const char * inFullName,
18894 uint16_t inType,
18895 uint16_t inClass,
18896 uint16_t inRDataLen,
18897 const void * inRDataPtr,
18898 uint32_t inTTL,
18899 void * inContext );
18900 static void DNSSD_API
18901 _ServiceBrowserBrowseCallback(
18902 DNSServiceRef inSDRef,
18903 DNSServiceFlags inFlags,
18904 uint32_t inInterfaceIndex,
18905 DNSServiceErrorType inError,
18906 const char * inName,
18907 const char * inRegType,
18908 const char * inDomain,
18909 void * inContext );
18910 static void DNSSD_API
18911 _ServiceBrowserResolveCallback(
18912 DNSServiceRef inSDRef,
18913 DNSServiceFlags inFlags,
18914 uint32_t inInterfaceIndex,
18915 DNSServiceErrorType inError,
18916 const char * inFullName,
18917 const char * inHostname,
18918 uint16_t inPort,
18919 uint16_t inTXTLen,
18920 const unsigned char * inTXTPtr,
18921 void * inContext );
18922 static void DNSSD_API
18923 _ServiceBrowserGAICallback(
18924 DNSServiceRef inSDRef,
18925 DNSServiceFlags inFlags,
18926 uint32_t inInterfaceIndex,
18927 DNSServiceErrorType inError,
18928 const char * inHostname,
18929 const struct sockaddr * inSockAddr,
18930 uint32_t inTTL,
18931 void * inContext );
18932 static OSStatus
18933 _ServiceBrowserAddServiceType(
18934 ServiceBrowserRef inBrowser,
18935 SBDomain * inDomain,
18936 const char * inName,
18937 uint32_t inIfIndex );
18938 static OSStatus
18939 _ServiceBrowserRemoveServiceType(
18940 ServiceBrowserRef inBrowser,
18941 SBDomain * inDomain,
18942 const char * inName,
18943 uint32_t inIfIndex );
18944 static OSStatus
18945 _ServiceBrowserAddServiceInstance(
18946 ServiceBrowserRef inBrowser,
18947 SBServiceBrowse * inBrowse,
18948 uint32_t inIfIndex,
18949 const char * inName,
18950 const char * inRegType,
18951 const char * inDomain,
18952 uint64_t inDiscoverTimeUs );
18953 static OSStatus
18954 _ServiceBrowserRemoveServiceInstance(
18955 ServiceBrowserRef inBrowser,
18956 SBServiceBrowse * inBrowse,
18957 const char * inName,
18958 uint32_t inIfIndex );
18959 static OSStatus
18960 _ServiceBrowserAddIPAddress(
18961 ServiceBrowserRef inBrowser,
18962 SBServiceInstance * inInstance,
18963 const struct sockaddr * inSockAddr,
18964 uint64_t inResolveTimeUs );
18965 static OSStatus
18966 _ServiceBrowserRemoveIPAddress(
18967 ServiceBrowserRef inBrowser,
18968 SBServiceInstance * inInstance,
18969 const struct sockaddr * inSockAddr );
18970 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
18971 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
18972 static void _SBDomainFree( SBDomain *inDomain );
18973 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType );
18974 static void _SBServiceTypeFree( SBServiceType *inType );
18975 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
18976 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse );
18977 static OSStatus
18978 _SBServiceInstanceCreate(
18979 const char * inName,
18980 uint32_t inIfIndex,
18981 uint64_t inDiscoverTimeUs,
18982 ServiceBrowserRef inBrowser,
18983 SBServiceInstance ** outInstance );
18984 static void _SBServiceInstanceFree( SBServiceInstance *inInstance );
18985 static OSStatus
18986 _SBIPAddressCreate(
18987 const struct sockaddr * inSockAddr,
18988 uint64_t inResolveTimeUs,
18989 SBIPAddress ** outIPAddress );
18990 static void _SBIPAddressFree( SBIPAddress *inIPAddress );
18991 static void _SBIPAddressFreeList( SBIPAddress *inList );
18992 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain );
18993 static void _SBRDomainFree( SBRDomain *inDomain );
18994 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
18995 static void _SBRServiceTypeFree( SBRServiceType *inType );
18996 static OSStatus
18997 _SBRServiceInstanceCreate(
18998 const char * inName,
18999 uint32_t inInterfaceIndex,
19000 const char * inHostname,
19001 uint16_t inPort,
19002 const uint8_t * inTXTPtr,
19003 size_t inTXTLen,
19004 uint64_t inDiscoverTimeUs,
19005 uint64_t inResolveTimeUs,
19006 SBRServiceInstance ** outInstance );
19007 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance );
19008 static OSStatus
19009 _SBRIPAddressCreate(
19010 const struct sockaddr * inSockAddr,
19011 uint64_t inResolveTimeUs,
19012 SBRIPAddress ** outIPAddress );
19013 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress );
19014
19015 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
19016
19017 CF_CLASS_DEFINE( ServiceBrowser );
19018
19019 static OSStatus
19020 ServiceBrowserCreate(
19021 dispatch_queue_t inQueue,
19022 uint32_t inInterfaceIndex,
19023 const char * inDomain,
19024 unsigned int inBrowseTimeSecs,
19025 Boolean inIncludeAWDL,
19026 ServiceBrowserRef * outBrowser )
19027 {
19028 OSStatus err;
19029 ServiceBrowserRef obj;
19030
19031 CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
19032
19033 ReplaceDispatchQueue( &obj->queue, inQueue );
19034 obj->ifIndex = inInterfaceIndex;
19035 if( inDomain )
19036 {
19037 obj->domain = strdup( inDomain );
19038 require_action( obj->domain, exit, err = kNoMemoryErr );
19039 }
19040 obj->browseTimeSecs = inBrowseTimeSecs;
19041 obj->includeAWDL = inIncludeAWDL;
19042
19043 *outBrowser = obj;
19044 obj = NULL;
19045 err = kNoErr;
19046
19047 exit:
19048 CFReleaseNullSafe( obj );
19049 return( err );
19050 }
19051
19052 //===========================================================================================================================
19053 // _ServiceBrowserFinalize
19054 //===========================================================================================================================
19055
19056 static void _ServiceBrowserFinalize( CFTypeRef inObj )
19057 {
19058 ServiceBrowserRef const me = (ServiceBrowserRef) inObj;
19059 StringListItem * serviceType;
19060
19061 dispatch_forget( &me->queue );
19062 check( !me->connection );
19063 check( !me->domainsQuery );
19064 ForgetMem( &me->domain );
19065 while( ( serviceType = me->serviceTypeList ) != NULL )
19066 {
19067 me->serviceTypeList = serviceType->next;
19068 ForgetMem( &serviceType->str );
19069 free( serviceType );
19070 }
19071 check( !me->domainList );
19072 check( !me->stopTimer );
19073 }
19074
19075 //===========================================================================================================================
19076 // ServiceBrowserStart
19077 //===========================================================================================================================
19078
19079 static void _ServiceBrowserStart( void *inContext );
19080
19081 static void ServiceBrowserStart( ServiceBrowserRef me )
19082 {
19083 CFRetain( me );
19084 dispatch_async_f( me->queue, me, _ServiceBrowserStart );
19085 }
19086
19087 static void _ServiceBrowserStart( void *inContext )
19088 {
19089 OSStatus err;
19090 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
19091
19092 err = DNSServiceCreateConnection( &me->connection );
19093 require_noerr( err, exit );
19094
19095 err = DNSServiceSetDispatchQueue( me->connection, me->queue );
19096 require_noerr( err, exit );
19097
19098 if( me->domain )
19099 {
19100 err = _ServiceBrowserAddDomain( me, me->domain );
19101 require_noerr( err, exit );
19102 }
19103 else
19104 {
19105 DNSServiceRef sdRef;
19106
19107 sdRef = me->connection;
19108 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly,
19109 "b._dns-sd._udp.local.", kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
19110 require_noerr( err, exit );
19111
19112 me->domainsQuery = sdRef;
19113 }
19114
19115 err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
19116 100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
19117 require_noerr( err, exit );
19118 dispatch_resume( me->stopTimer );
19119
19120 exit:
19121 if( err ) _ServiceBrowserStop( me, err );
19122 }
19123
19124 //===========================================================================================================================
19125 // ServiceBrowserAddServiceType
19126 //===========================================================================================================================
19127
19128 static OSStatus ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
19129 {
19130 OSStatus err;
19131 StringListItem * item;
19132 StringListItem ** itemPtr;
19133 StringListItem * newItem = NULL;
19134
19135 for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
19136 {
19137 if( strcmp( item->str, inServiceType ) == 0 ) break;
19138 }
19139 if( !item )
19140 {
19141 newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
19142 require_action( newItem, exit, err = kNoMemoryErr );
19143
19144 newItem->str = strdup( inServiceType );
19145 require_action( newItem->str, exit, err = kNoMemoryErr );
19146
19147 *itemPtr = newItem;
19148 newItem = NULL;
19149 }
19150 err = kNoErr;
19151
19152 exit:
19153 FreeNullSafe( newItem );
19154 return( err );
19155 }
19156
19157 //===========================================================================================================================
19158 // ServiceBrowserSetCallback
19159 //===========================================================================================================================
19160
19161 static void ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
19162 {
19163 me->userCallback = inCallback;
19164 me->userContext = inContext;
19165 }
19166
19167 //===========================================================================================================================
19168 // ServiceBrowserResultsRetain
19169 //===========================================================================================================================
19170
19171 static void ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
19172 {
19173 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
19174
19175 atomic_add_32( &results->refCount, 1 );
19176 }
19177
19178 //===========================================================================================================================
19179 // ServiceBrowserResultsRelease
19180 //===========================================================================================================================
19181
19182 static void ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
19183 {
19184 ServiceBrowserResultsPrivate * const results = (ServiceBrowserResultsPrivate *) inResults;
19185 SBRDomain * domain;
19186
19187 if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
19188 {
19189 while( ( domain = inResults->domainList ) != NULL )
19190 {
19191 inResults->domainList = domain->next;
19192 _SBRDomainFree( domain );
19193 }
19194 free( inResults );
19195 }
19196 }
19197
19198 //===========================================================================================================================
19199 // _ServiceBrowserStop
19200 //===========================================================================================================================
19201
19202 static void _ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
19203 {
19204 OSStatus err;
19205 SBDomain * d;
19206 SBServiceType * t;
19207 SBServiceBrowse * b;
19208 SBServiceInstance * i;
19209
19210 dispatch_source_forget( &me->stopTimer );
19211 DNSServiceForget( &me->domainsQuery );
19212 for( d = me->domainList; d; d = d->next )
19213 {
19214 DNSServiceForget( &d->servicesQuery );
19215 for( t = d->typeList; t; t = t->next )
19216 {
19217 for( b = t->browseList; b; b = b->next )
19218 {
19219 DNSServiceForget( &b->browse );
19220 for( i = b->instanceList; i; i = i->next )
19221 {
19222 DNSServiceForget( &i->resolve );
19223 DNSServiceForget( &i->getAddrInfo );
19224 }
19225 }
19226 }
19227 }
19228 DNSServiceForget( &me->connection );
19229
19230 if( me->userCallback )
19231 {
19232 ServiceBrowserResults * results = NULL;
19233
19234 err = _ServiceBrowserCreateResults( me, &results );
19235 if( !err ) err = inError;
19236
19237 me->userCallback( results, err, me->userContext );
19238 me->userCallback = NULL;
19239 me->userContext = NULL;
19240 if( results ) ServiceBrowserResultsRelease( results );
19241 }
19242
19243 while( ( d = me->domainList ) != NULL )
19244 {
19245 me->domainList = d->next;
19246 _SBDomainFree( d );
19247 }
19248 CFRelease( me );
19249 }
19250
19251 //===========================================================================================================================
19252 // _ServiceBrowserAddDomain
19253 //===========================================================================================================================
19254
19255 static OSStatus _ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
19256 {
19257 OSStatus err;
19258 SBDomain * domain;
19259 SBDomain ** domainPtr;
19260 SBDomain * newDomain = NULL;
19261
19262 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
19263 {
19264 if( strcasecmp( domain->name, inDomain ) == 0 ) break;
19265 }
19266 require_action_quiet( !domain, exit, err = kDuplicateErr );
19267
19268 err = _SBDomainCreate( inDomain, me, &newDomain );
19269 require_noerr_quiet( err, exit );
19270
19271 if( me->serviceTypeList )
19272 {
19273 const StringListItem * item;
19274
19275 for( item = me->serviceTypeList; item; item = item->next )
19276 {
19277 err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
19278 if( err == kDuplicateErr ) err = kNoErr;
19279 require_noerr( err, exit );
19280 }
19281 }
19282 else
19283 {
19284 char * recordName;
19285 DNSServiceFlags flags;
19286 DNSServiceRef sdRef;
19287
19288 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
19289 require_action( recordName, exit, err = kNoMemoryErr );
19290
19291 flags = kDNSServiceFlagsShareConnection;
19292 if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
19293
19294 sdRef = newDomain->browser->connection;
19295 err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
19296 _ServiceBrowserServicesQueryCallback, newDomain );
19297 free( recordName );
19298 require_noerr( err, exit );
19299
19300 newDomain->servicesQuery = sdRef;
19301 }
19302
19303 *domainPtr = newDomain;
19304 newDomain = NULL;
19305 err = kNoErr;
19306
19307 exit:
19308 if( newDomain ) _SBDomainFree( newDomain );
19309 return( err );
19310 }
19311
19312 //===========================================================================================================================
19313 // _ServiceBrowserRemoveDomain
19314 //===========================================================================================================================
19315
19316 static OSStatus _ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
19317 {
19318 OSStatus err;
19319 SBDomain * domain;
19320 SBDomain ** domainPtr;
19321
19322 for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
19323 {
19324 if( strcasecmp( domain->name, inName ) == 0 ) break;
19325 }
19326
19327 if( domain )
19328 {
19329 *domainPtr = domain->next;
19330 _SBDomainFree( domain );
19331 err = kNoErr;
19332 }
19333 else
19334 {
19335 err = kNotFoundErr;
19336 }
19337
19338 return( err );
19339 }
19340
19341 //===========================================================================================================================
19342 // _ServiceBrowserTimerHandler
19343 //===========================================================================================================================
19344
19345 static void _ServiceBrowserTimerHandler( void *inContext )
19346 {
19347 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
19348
19349 _ServiceBrowserStop( me, kNoErr );
19350 }
19351
19352 //===========================================================================================================================
19353 // _ServiceBrowserDomainsQueryCallback
19354 //===========================================================================================================================
19355
19356 static void DNSSD_API
19357 _ServiceBrowserDomainsQueryCallback(
19358 DNSServiceRef inSDRef,
19359 DNSServiceFlags inFlags,
19360 uint32_t inInterfaceIndex,
19361 DNSServiceErrorType inError,
19362 const char * inFullName,
19363 uint16_t inType,
19364 uint16_t inClass,
19365 uint16_t inRDataLen,
19366 const void * inRDataPtr,
19367 uint32_t inTTL,
19368 void * inContext )
19369 {
19370 ServiceBrowserRef const me = (ServiceBrowserRef) inContext;
19371 OSStatus err;
19372 char domainStr[ kDNSServiceMaxDomainName ];
19373
19374 Unused( inSDRef );
19375 Unused( inInterfaceIndex );
19376 Unused( inFullName );
19377 Unused( inType );
19378 Unused( inClass );
19379 Unused( inTTL );
19380
19381 require_noerr( inError, exit );
19382
19383 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
19384 require_noerr( err, exit );
19385
19386 if( inFlags & kDNSServiceFlagsAdd )
19387 {
19388 err = _ServiceBrowserAddDomain( me, domainStr );
19389 if( err == kDuplicateErr ) err = kNoErr;
19390 require_noerr( err, exit );
19391 }
19392 else
19393 {
19394 err = _ServiceBrowserRemoveDomain( me, domainStr );
19395 if( err == kNotFoundErr ) err = kNoErr;
19396 require_noerr( err, exit );
19397 }
19398
19399 exit:
19400 return;
19401 }
19402
19403 //===========================================================================================================================
19404 // _ServiceBrowserServicesQueryCallback
19405 //===========================================================================================================================
19406
19407 static void DNSSD_API
19408 _ServiceBrowserServicesQueryCallback(
19409 DNSServiceRef inSDRef,
19410 DNSServiceFlags inFlags,
19411 uint32_t inInterfaceIndex,
19412 DNSServiceErrorType inError,
19413 const char * inFullName,
19414 uint16_t inType,
19415 uint16_t inClass,
19416 uint16_t inRDataLen,
19417 const void * inRDataPtr,
19418 uint32_t inTTL,
19419 void * inContext )
19420 {
19421 OSStatus err;
19422 SBDomain * const domain = (SBDomain *) inContext;
19423 ServiceBrowserRef const me = domain->browser;
19424 const uint8_t * src;
19425 const uint8_t * end;
19426 uint8_t * dst;
19427 int i;
19428 uint8_t serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
19429 char serviceTypeStr[ kDNSServiceMaxDomainName ];
19430
19431 Unused( inSDRef );
19432 Unused( inFullName );
19433 Unused( inTTL );
19434 Unused( inType );
19435 Unused( inClass );
19436
19437 require_noerr( inError, exit );
19438
19439 check( inType == kDNSServiceType_PTR );
19440 check( inClass == kDNSServiceClass_IN );
19441
19442 // The first two labels of the domain name in the RDATA describe a service type.
19443 // See <https://tools.ietf.org/html/rfc6763#section-9>.
19444
19445 src = (const uint8_t *) inRDataPtr;
19446 end = src + inRDataLen;
19447 dst = serviceType;
19448 for( i = 0; i < 2; ++i )
19449 {
19450 size_t labelLen;
19451
19452 require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
19453
19454 labelLen = *src;
19455 require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
19456 require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
19457
19458 memcpy( dst, src, 1 + labelLen );
19459 src += 1 + labelLen;
19460 dst += 1 + labelLen;
19461 }
19462 *dst = 0;
19463
19464 err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
19465 require_noerr( err, exit );
19466
19467 if( inFlags & kDNSServiceFlagsAdd )
19468 {
19469 err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
19470 if( err == kDuplicateErr ) err = kNoErr;
19471 require_noerr( err, exit );
19472 }
19473 else
19474 {
19475 err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
19476 if( err == kNotFoundErr ) err = kNoErr;
19477 require_noerr( err, exit );
19478 }
19479
19480 exit:
19481 return;
19482 }
19483
19484 //===========================================================================================================================
19485 // _ServiceBrowserBrowseCallback
19486 //===========================================================================================================================
19487
19488 static void DNSSD_API
19489 _ServiceBrowserBrowseCallback(
19490 DNSServiceRef inSDRef,
19491 DNSServiceFlags inFlags,
19492 uint32_t inInterfaceIndex,
19493 DNSServiceErrorType inError,
19494 const char * inName,
19495 const char * inRegType,
19496 const char * inDomain,
19497 void * inContext )
19498 {
19499 OSStatus err;
19500 const uint64_t nowTicks = UpTicks();
19501 SBServiceBrowse * const browse = (SBServiceBrowse *) inContext;
19502 ServiceBrowserRef const me = (ServiceBrowserRef) browse->browser;
19503
19504 Unused( inSDRef );
19505
19506 require_noerr( inError, exit );
19507
19508 if( inFlags & kDNSServiceFlagsAdd )
19509 {
19510 err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
19511 UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
19512 if( err == kDuplicateErr ) err = kNoErr;
19513 require_noerr( err, exit );
19514 }
19515 else
19516 {
19517 err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
19518 if( err == kNotFoundErr ) err = kNoErr;
19519 require_noerr( err, exit );
19520 }
19521
19522 exit:
19523 return;
19524 }
19525
19526 //===========================================================================================================================
19527 // _ServiceBrowserResolveCallback
19528 //===========================================================================================================================
19529
19530 static void DNSSD_API
19531 _ServiceBrowserResolveCallback(
19532 DNSServiceRef inSDRef,
19533 DNSServiceFlags inFlags,
19534 uint32_t inInterfaceIndex,
19535 DNSServiceErrorType inError,
19536 const char * inFullName,
19537 const char * inHostname,
19538 uint16_t inPort,
19539 uint16_t inTXTLen,
19540 const unsigned char * inTXTPtr,
19541 void * inContext )
19542 {
19543 OSStatus err;
19544 const uint64_t nowTicks = UpTicks();
19545 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
19546 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
19547
19548 Unused( inSDRef );
19549 Unused( inFlags );
19550 Unused( inInterfaceIndex );
19551 Unused( inFullName );
19552
19553 require_noerr( inError, exit );
19554
19555 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
19556 {
19557 FreeNullSafe( instance->txtPtr );
19558 instance->txtPtr = memdup( inTXTPtr, inTXTLen );
19559 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
19560
19561 instance->txtLen = inTXTLen;
19562 }
19563
19564 instance->port = ntohs( inPort );
19565
19566 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
19567 {
19568 DNSServiceRef sdRef;
19569
19570 if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
19571
19572 err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
19573 require_noerr( err, exit );
19574
19575 DNSServiceForget( &instance->getAddrInfo );
19576 ForgetSBIPAddressList( &instance->ipaddrList );
19577
19578 sdRef = me->connection;
19579 instance->gaiStartTicks = UpTicks();
19580 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
19581 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback, instance );
19582 require_noerr( err, exit );
19583
19584 instance->getAddrInfo = sdRef;
19585 }
19586
19587 exit:
19588 return;
19589 }
19590
19591 //===========================================================================================================================
19592 // _ServiceBrowserGAICallback
19593 //===========================================================================================================================
19594
19595 static void DNSSD_API
19596 _ServiceBrowserGAICallback(
19597 DNSServiceRef inSDRef,
19598 DNSServiceFlags inFlags,
19599 uint32_t inInterfaceIndex,
19600 DNSServiceErrorType inError,
19601 const char * inHostname,
19602 const struct sockaddr * inSockAddr,
19603 uint32_t inTTL,
19604 void * inContext )
19605 {
19606 OSStatus err;
19607 const uint64_t nowTicks = UpTicks();
19608 SBServiceInstance * const instance = (SBServiceInstance *) inContext;
19609 ServiceBrowserRef const me = (ServiceBrowserRef) instance->browser;
19610
19611 Unused( inSDRef );
19612 Unused( inInterfaceIndex );
19613 Unused( inHostname );
19614 Unused( inTTL );
19615
19616 require_noerr( inError, exit );
19617
19618 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
19619 {
19620 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
19621 goto exit;
19622 }
19623
19624 if( inFlags & kDNSServiceFlagsAdd )
19625 {
19626 err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
19627 UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
19628 if( err == kDuplicateErr ) err = kNoErr;
19629 require_noerr( err, exit );
19630 }
19631 else
19632 {
19633 err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
19634 if( err == kNotFoundErr ) err = kNoErr;
19635 require_noerr( err, exit );
19636 }
19637
19638 exit:
19639 return;
19640 }
19641
19642 //===========================================================================================================================
19643 // _ServiceBrowserAddServiceType
19644 //===========================================================================================================================
19645
19646 static OSStatus
19647 _ServiceBrowserAddServiceType(
19648 ServiceBrowserRef me,
19649 SBDomain * inDomain,
19650 const char * inName,
19651 uint32_t inIfIndex )
19652 {
19653 OSStatus err;
19654 SBServiceType * type;
19655 SBServiceType ** typePtr;
19656 SBServiceType * newType = NULL;
19657 SBServiceBrowse * browse;
19658 SBServiceBrowse ** browsePtr;
19659 SBServiceBrowse * newBrowse = NULL;
19660 DNSServiceRef sdRef;
19661 DNSServiceFlags flags;
19662
19663 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
19664 {
19665 if( strcasecmp( type->name, inName ) == 0 ) break;
19666 }
19667 if( !type )
19668 {
19669 err = _SBServiceTypeCreate( inName, &newType );
19670 require_noerr_quiet( err, exit );
19671
19672 type = newType;
19673 }
19674
19675 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
19676 {
19677 if( browse->ifIndex == inIfIndex ) break;
19678 }
19679 require_action_quiet( !browse, exit, err = kDuplicateErr );
19680
19681 err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
19682 require_noerr_quiet( err, exit );
19683
19684 flags = kDNSServiceFlagsShareConnection;
19685 if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
19686
19687 sdRef = me->connection;
19688 newBrowse->startTicks = UpTicks();
19689 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
19690 newBrowse );
19691 require_noerr( err, exit );
19692
19693 newBrowse->browse = sdRef;
19694 *browsePtr = newBrowse;
19695 newBrowse = NULL;
19696
19697 if( newType )
19698 {
19699 *typePtr = newType;
19700 newType = NULL;
19701 }
19702
19703 exit:
19704 if( newBrowse ) _SBServiceBrowseFree( newBrowse );
19705 if( newType ) _SBServiceTypeFree( newType );
19706 return( err );
19707 }
19708
19709 //===========================================================================================================================
19710 // _ServiceBrowserRemoveServiceType
19711 //===========================================================================================================================
19712
19713 static OSStatus
19714 _ServiceBrowserRemoveServiceType(
19715 ServiceBrowserRef me,
19716 SBDomain * inDomain,
19717 const char * inName,
19718 uint32_t inIfIndex )
19719 {
19720 OSStatus err;
19721 SBServiceType * type;
19722 SBServiceType ** typePtr;
19723 SBServiceBrowse * browse;
19724 SBServiceBrowse ** browsePtr;
19725
19726 Unused( me );
19727
19728 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
19729 {
19730 if( strcasecmp( type->name, inName ) == 0 ) break;
19731 }
19732 require_action_quiet( type, exit, err = kNotFoundErr );
19733
19734 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
19735 {
19736 if( browse->ifIndex == inIfIndex ) break;
19737 }
19738 require_action_quiet( browse, exit, err = kNotFoundErr );
19739
19740 *browsePtr = browse->next;
19741 _SBServiceBrowseFree( browse );
19742 if( !type->browseList )
19743 {
19744 *typePtr = type->next;
19745 _SBServiceTypeFree( type );
19746 }
19747 err = kNoErr;
19748
19749 exit:
19750 return( err );
19751 }
19752
19753 //===========================================================================================================================
19754 // _ServiceBrowserAddServiceInstance
19755 //===========================================================================================================================
19756
19757 static OSStatus
19758 _ServiceBrowserAddServiceInstance(
19759 ServiceBrowserRef me,
19760 SBServiceBrowse * inBrowse,
19761 uint32_t inIfIndex,
19762 const char * inName,
19763 const char * inRegType,
19764 const char * inDomain,
19765 uint64_t inDiscoverTimeUs )
19766 {
19767 OSStatus err;
19768 DNSServiceRef sdRef;
19769 SBServiceInstance * instance;
19770 SBServiceInstance ** instancePtr;
19771 SBServiceInstance * newInstance = NULL;
19772
19773 for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
19774 {
19775 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
19776 }
19777 require_action_quiet( !instance, exit, err = kDuplicateErr );
19778
19779 err = _SBServiceInstanceCreate( inName, inIfIndex, inDiscoverTimeUs, me, &newInstance );
19780 require_noerr_quiet( err, exit );
19781
19782 sdRef = me->connection;
19783 newInstance->resolveStartTicks = UpTicks();
19784 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
19785 _ServiceBrowserResolveCallback, newInstance );
19786 require_noerr( err, exit );
19787
19788 newInstance->resolve = sdRef;
19789 *instancePtr = newInstance;
19790 newInstance = NULL;
19791
19792 exit:
19793 if( newInstance ) _SBServiceInstanceFree( newInstance );
19794 return( err );
19795 }
19796
19797 //===========================================================================================================================
19798 // _ServiceBrowserRemoveServiceInstance
19799 //===========================================================================================================================
19800
19801 static OSStatus
19802 _ServiceBrowserRemoveServiceInstance(
19803 ServiceBrowserRef me,
19804 SBServiceBrowse * inBrowse,
19805 const char * inName,
19806 uint32_t inIfIndex )
19807 {
19808 OSStatus err;
19809 SBServiceInstance * instance;
19810 SBServiceInstance ** ptr;
19811
19812 Unused( me );
19813
19814 for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
19815 {
19816 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
19817 }
19818 require_action_quiet( instance, exit, err = kNotFoundErr );
19819
19820 *ptr = instance->next;
19821 _SBServiceInstanceFree( instance );
19822 err = kNoErr;
19823
19824 exit:
19825 return( err );
19826 }
19827
19828 //===========================================================================================================================
19829 // _ServiceBrowserAddIPAddress
19830 //===========================================================================================================================
19831
19832 static OSStatus
19833 _ServiceBrowserAddIPAddress(
19834 ServiceBrowserRef me,
19835 SBServiceInstance * inInstance,
19836 const struct sockaddr * inSockAddr,
19837 uint64_t inResolveTimeUs )
19838 {
19839 OSStatus err;
19840 SBIPAddress * ipaddr;
19841 SBIPAddress ** ipaddrPtr;
19842 SBIPAddress * newIPAddr = NULL;
19843
19844 Unused( me );
19845
19846 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
19847 {
19848 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
19849 err = kTypeErr;
19850 goto exit;
19851 }
19852
19853 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
19854 {
19855 if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
19856 }
19857 require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
19858
19859 err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
19860 require_noerr_quiet( err, exit );
19861
19862 *ipaddrPtr = newIPAddr;
19863 newIPAddr = NULL;
19864 err = kNoErr;
19865
19866 exit:
19867 if( newIPAddr ) _SBIPAddressFree( newIPAddr );
19868 return( err );
19869 }
19870
19871 //===========================================================================================================================
19872 // _ServiceBrowserRemoveIPAddress
19873 //===========================================================================================================================
19874
19875 static OSStatus
19876 _ServiceBrowserRemoveIPAddress(
19877 ServiceBrowserRef me,
19878 SBServiceInstance * inInstance,
19879 const struct sockaddr * inSockAddr )
19880 {
19881 OSStatus err;
19882 SBIPAddress * ipaddr;
19883 SBIPAddress ** ipaddrPtr;
19884
19885 Unused( me );
19886
19887 for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
19888 {
19889 if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
19890 }
19891 require_action_quiet( ipaddr, exit, err = kNotFoundErr );
19892
19893 *ipaddrPtr = ipaddr->next;
19894 _SBIPAddressFree( ipaddr );
19895 err = kNoErr;
19896
19897 exit:
19898 return( err );
19899 }
19900
19901 //===========================================================================================================================
19902 // _ServiceBrowserCreateResults
19903 //===========================================================================================================================
19904
19905 static OSStatus _ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
19906 {
19907 OSStatus err;
19908 SBDomain * d;
19909 SBServiceType * t;
19910 SBServiceBrowse * b;
19911 SBServiceInstance * i;
19912 SBIPAddress * a;
19913 ServiceBrowserResultsPrivate * results;
19914 SBRDomain ** domainPtr;
19915
19916 results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
19917 require_action( results, exit, err = kNoMemoryErr );
19918
19919 results->refCount = 1;
19920
19921 domainPtr = &results->domainList;
19922 for( d = me->domainList; d; d = d->next )
19923 {
19924 SBRDomain * domain;
19925 SBRServiceType ** typePtr;
19926
19927 err = _SBRDomainCreate( d->name, &domain );
19928 require_noerr_quiet( err, exit );
19929 *domainPtr = domain;
19930 domainPtr = &domain->next;
19931
19932 typePtr = &domain->typeList;
19933 for( t = d->typeList; t; t = t->next )
19934 {
19935 SBRServiceType * type;
19936 SBRServiceInstance ** instancePtr;
19937
19938 err = _SBRServiceTypeCreate( t->name, &type );
19939 require_noerr_quiet( err, exit );
19940 *typePtr = type;
19941 typePtr = &type->next;
19942
19943 instancePtr = &type->instanceList;
19944 for( b = t->browseList; b; b = b->next )
19945 {
19946 for( i = b->instanceList; i; i = i->next )
19947 {
19948 SBRServiceInstance * instance;
19949 SBRIPAddress ** ipaddrPtr;
19950
19951 err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
19952 i->discoverTimeUs, i->resolveTimeUs, &instance );
19953 require_noerr_quiet( err, exit );
19954 *instancePtr = instance;
19955 instancePtr = &instance->next;
19956
19957 ipaddrPtr = &instance->ipaddrList;
19958 for( a = i->ipaddrList; a; a = a->next )
19959 {
19960 SBRIPAddress * ipaddr;
19961
19962 err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
19963 require_noerr_quiet( err, exit );
19964
19965 *ipaddrPtr = ipaddr;
19966 ipaddrPtr = &ipaddr->next;
19967 }
19968 }
19969 }
19970 }
19971 }
19972
19973 *outResults = (ServiceBrowserResults *) results;
19974 results = NULL;
19975 err = kNoErr;
19976
19977 exit:
19978 if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
19979 return( err );
19980 }
19981
19982 //===========================================================================================================================
19983 // _SBDomainCreate
19984 //===========================================================================================================================
19985
19986 static OSStatus _SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
19987 {
19988 OSStatus err;
19989 SBDomain * obj;
19990
19991 obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
19992 require_action( obj, exit, err = kNoMemoryErr );
19993
19994 obj->name = strdup( inName );
19995 require_action( obj->name, exit, err = kNoMemoryErr );
19996
19997 obj->browser = inBrowser;
19998
19999 *outDomain = obj;
20000 obj = NULL;
20001 err = kNoErr;
20002
20003 exit:
20004 if( obj ) _SBDomainFree( obj );
20005 return( err );
20006 }
20007
20008 //===========================================================================================================================
20009 // _SBDomainFree
20010 //===========================================================================================================================
20011
20012 static void _SBDomainFree( SBDomain *inDomain )
20013 {
20014 SBServiceType * type;
20015
20016 ForgetMem( &inDomain->name );
20017 DNSServiceForget( &inDomain->servicesQuery );
20018 while( ( type = inDomain->typeList ) != NULL )
20019 {
20020 inDomain->typeList = type->next;
20021 _SBServiceTypeFree( type );
20022 }
20023 free( inDomain );
20024 }
20025
20026 //===========================================================================================================================
20027 // _SBServiceTypeCreate
20028 //===========================================================================================================================
20029
20030 static OSStatus _SBServiceTypeCreate( const char *inName, SBServiceType **outType )
20031 {
20032 OSStatus err;
20033 SBServiceType * obj;
20034
20035 obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
20036 require_action( obj, exit, err = kNoMemoryErr );
20037
20038 obj->name = strdup( inName );
20039 require_action( obj->name, exit, err = kNoMemoryErr );
20040
20041 *outType = obj;
20042 obj = NULL;
20043 err = kNoErr;
20044
20045 exit:
20046 if( obj ) _SBServiceTypeFree( obj );
20047 return( err );
20048 }
20049
20050 //===========================================================================================================================
20051 // _SBServiceTypeFree
20052 //===========================================================================================================================
20053
20054 static void _SBServiceTypeFree( SBServiceType *inType )
20055 {
20056 SBServiceBrowse * browse;
20057
20058 ForgetMem( &inType->name );
20059 while( ( browse = inType->browseList ) != NULL )
20060 {
20061 inType->browseList = browse->next;
20062 _SBServiceBrowseFree( browse );
20063 }
20064 free( inType );
20065 }
20066
20067 //===========================================================================================================================
20068 // _SBServiceBrowseCreate
20069 //===========================================================================================================================
20070
20071 static OSStatus _SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
20072 {
20073 OSStatus err;
20074 SBServiceBrowse * obj;
20075
20076 obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
20077 require_action( obj, exit, err = kNoMemoryErr );
20078
20079 obj->ifIndex = inIfIndex;
20080 obj->browser = inBrowser;
20081 *outBrowse = obj;
20082 err = kNoErr;
20083
20084 exit:
20085 return( err );
20086 }
20087
20088 //===========================================================================================================================
20089 // _SBServiceBrowseFree
20090 //===========================================================================================================================
20091
20092 static void _SBServiceBrowseFree( SBServiceBrowse *inBrowse )
20093 {
20094 SBServiceInstance * instance;
20095
20096 DNSServiceForget( &inBrowse->browse );
20097 while( ( instance = inBrowse->instanceList ) != NULL )
20098 {
20099 inBrowse->instanceList = instance->next;
20100 _SBServiceInstanceFree( instance );
20101 }
20102 free( inBrowse );
20103 }
20104
20105 //===========================================================================================================================
20106 // _SBServiceInstanceCreate
20107 //===========================================================================================================================
20108
20109 static OSStatus
20110 _SBServiceInstanceCreate(
20111 const char * inName,
20112 uint32_t inIfIndex,
20113 uint64_t inDiscoverTimeUs,
20114 ServiceBrowserRef inBrowser,
20115 SBServiceInstance ** outInstance )
20116 {
20117 OSStatus err;
20118 SBServiceInstance * obj;
20119
20120 obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
20121 require_action( obj, exit, err = kNoMemoryErr );
20122
20123 obj->name = strdup( inName );
20124 require_action( obj->name, exit, err = kNoMemoryErr );
20125
20126 obj->ifIndex = inIfIndex;
20127 obj->discoverTimeUs = inDiscoverTimeUs;
20128 obj->browser = inBrowser;
20129
20130 *outInstance = obj;
20131 obj = NULL;
20132 err = kNoErr;
20133
20134 exit:
20135 if( obj ) _SBServiceInstanceFree( obj );
20136 return( err );
20137 }
20138
20139 //===========================================================================================================================
20140 // _SBServiceInstanceFree
20141 //===========================================================================================================================
20142
20143 static void _SBServiceInstanceFree( SBServiceInstance *inInstance )
20144 {
20145 ForgetMem( &inInstance->name );
20146 DNSServiceForget( &inInstance->resolve );
20147 ForgetMem( &inInstance->hostname );
20148 ForgetMem( &inInstance->txtPtr );
20149 DNSServiceForget( &inInstance->getAddrInfo );
20150 ForgetSBIPAddressList( &inInstance->ipaddrList );
20151 free( inInstance );
20152 }
20153
20154 //===========================================================================================================================
20155 // _SBIPAddressCreate
20156 //===========================================================================================================================
20157
20158 static OSStatus _SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
20159 {
20160 OSStatus err;
20161 SBIPAddress * obj;
20162
20163 obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
20164 require_action( obj, exit, err = kNoMemoryErr );
20165
20166 SockAddrCopy( inSockAddr, &obj->sip );
20167 obj->resolveTimeUs = inResolveTimeUs;
20168
20169 *outIPAddress = obj;
20170 err = kNoErr;
20171
20172 exit:
20173 return( err );
20174 }
20175
20176 //===========================================================================================================================
20177 // _SBIPAddressFree
20178 //===========================================================================================================================
20179
20180 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
20181 {
20182 free( inIPAddress );
20183 }
20184
20185 //===========================================================================================================================
20186 // _SBIPAddressFreeList
20187 //===========================================================================================================================
20188
20189 static void _SBIPAddressFreeList( SBIPAddress *inList )
20190 {
20191 SBIPAddress * ipaddr;
20192
20193 while( ( ipaddr = inList ) != NULL )
20194 {
20195 inList = ipaddr->next;
20196 _SBIPAddressFree( ipaddr );
20197 }
20198 }
20199
20200 //===========================================================================================================================
20201 // _SBRDomainCreate
20202 //===========================================================================================================================
20203
20204 static OSStatus _SBRDomainCreate( const char *inName, SBRDomain **outDomain )
20205 {
20206 OSStatus err;
20207 SBRDomain * obj;
20208
20209 obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
20210 require_action( obj, exit, err = kNoMemoryErr );
20211
20212 obj->name = strdup( inName );
20213 require_action( obj->name, exit, err = kNoMemoryErr );
20214
20215 *outDomain = obj;
20216 obj = NULL;
20217 err = kNoErr;
20218
20219 exit:
20220 if( obj ) _SBRDomainFree( obj );
20221 return( err );
20222 }
20223
20224 //===========================================================================================================================
20225 // _SBRDomainFree
20226 //===========================================================================================================================
20227
20228 static void _SBRDomainFree( SBRDomain *inDomain )
20229 {
20230 SBRServiceType * type;
20231
20232 ForgetMem( &inDomain->name );
20233 while( ( type = inDomain->typeList ) != NULL )
20234 {
20235 inDomain->typeList = type->next;
20236 _SBRServiceTypeFree( type );
20237 }
20238 free( inDomain );
20239 }
20240
20241 //===========================================================================================================================
20242 // _SBRServiceTypeCreate
20243 //===========================================================================================================================
20244
20245 static OSStatus _SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
20246 {
20247 OSStatus err;
20248 SBRServiceType * obj;
20249
20250 obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
20251 require_action( obj, exit, err = kNoMemoryErr );
20252
20253 obj->name = strdup( inName );
20254 require_action( obj->name, exit, err = kNoMemoryErr );
20255
20256 *outType = obj;
20257 obj = NULL;
20258 err = kNoErr;
20259
20260 exit:
20261 if( obj ) _SBRServiceTypeFree( obj );
20262 return( err );
20263 }
20264
20265 //===========================================================================================================================
20266 // _SBRServiceTypeFree
20267 //===========================================================================================================================
20268
20269 static void _SBRServiceTypeFree( SBRServiceType *inType )
20270 {
20271 SBRServiceInstance * instance;
20272
20273 ForgetMem( &inType->name );
20274 while( ( instance = inType->instanceList ) != NULL )
20275 {
20276 inType->instanceList = instance->next;
20277 _SBRServiceInstanceFree( instance );
20278 }
20279 free( inType );
20280 }
20281
20282 //===========================================================================================================================
20283 // _SBRServiceInstanceCreate
20284 //===========================================================================================================================
20285
20286 static OSStatus
20287 _SBRServiceInstanceCreate(
20288 const char * inName,
20289 uint32_t inInterfaceIndex,
20290 const char * inHostname,
20291 uint16_t inPort,
20292 const uint8_t * inTXTPtr,
20293 size_t inTXTLen,
20294 uint64_t inDiscoverTimeUs,
20295 uint64_t inResolveTimeUs,
20296 SBRServiceInstance ** outInstance )
20297 {
20298 OSStatus err;
20299 SBRServiceInstance * obj;
20300
20301 obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
20302 require_action( obj, exit, err = kNoMemoryErr );
20303
20304 obj->name = strdup( inName );
20305 require_action( obj->name, exit, err = kNoMemoryErr );
20306
20307 if( inHostname )
20308 {
20309 obj->hostname = strdup( inHostname );
20310 require_action( obj->hostname, exit, err = kNoMemoryErr );
20311 }
20312 if( inTXTLen > 0 )
20313 {
20314 obj->txtPtr = (uint8_t *) memdup( inTXTPtr, inTXTLen );
20315 require_action( obj->txtPtr, exit, err = kNoMemoryErr );
20316 obj->txtLen = inTXTLen;
20317 }
20318 obj->discoverTimeUs = inDiscoverTimeUs;
20319 obj->resolveTimeUs = inResolveTimeUs;
20320 obj->ifIndex = inInterfaceIndex;
20321 obj->port = inPort;
20322
20323 *outInstance = obj;
20324 obj = NULL;
20325 err = kNoErr;
20326
20327 exit:
20328 if( obj ) _SBRServiceInstanceFree( obj );
20329 return( err );
20330 }
20331
20332 //===========================================================================================================================
20333 // _SBRServiceInstanceFree
20334 //===========================================================================================================================
20335
20336 static void _SBRServiceInstanceFree( SBRServiceInstance *inInstance )
20337 {
20338 SBRIPAddress * ipaddr;
20339
20340 ForgetMem( &inInstance->name );
20341 ForgetMem( &inInstance->hostname );
20342 ForgetMem( &inInstance->txtPtr );
20343 while( ( ipaddr = inInstance->ipaddrList ) != NULL )
20344 {
20345 inInstance->ipaddrList = ipaddr->next;
20346 _SBRIPAddressFree( ipaddr );
20347 }
20348 free( inInstance );
20349 }
20350
20351 //===========================================================================================================================
20352 // _SBRIPAddressCreate
20353 //===========================================================================================================================
20354
20355 static OSStatus
20356 _SBRIPAddressCreate(
20357 const struct sockaddr * inSockAddr,
20358 uint64_t inResolveTimeUs,
20359 SBRIPAddress ** outIPAddress )
20360 {
20361 OSStatus err;
20362 SBRIPAddress * obj;
20363
20364 obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
20365 require_action( obj, exit, err = kNoMemoryErr );
20366
20367 SockAddrCopy( inSockAddr, &obj->sip );
20368 obj->resolveTimeUs = inResolveTimeUs;
20369
20370 *outIPAddress = obj;
20371 err = kNoErr;
20372
20373 exit:
20374 return( err );
20375 }
20376
20377 //===========================================================================================================================
20378 // _SBRIPAddressFree
20379 //===========================================================================================================================
20380
20381 static void _SBRIPAddressFree( SBRIPAddress *inIPAddress )
20382 {
20383 free( inIPAddress );
20384 }
20385
20386 //===========================================================================================================================
20387 // SocketWriteAll
20388 //
20389 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
20390 //===========================================================================================================================
20391
20392 OSStatus SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
20393 {
20394 OSStatus err;
20395 const uint8_t * src;
20396 const uint8_t * end;
20397 fd_set writeSet;
20398 struct timeval timeout;
20399 ssize_t n;
20400
20401 FD_ZERO( &writeSet );
20402 src = (const uint8_t *) inData;
20403 end = src + inSize;
20404 while( src < end )
20405 {
20406 FD_SET( inSock, &writeSet );
20407 timeout.tv_sec = inTimeoutSecs;
20408 timeout.tv_usec = 0;
20409 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
20410 if( n == 0 ) { err = kTimeoutErr; goto exit; }
20411 err = map_socket_value_errno( inSock, n > 0, n );
20412 require_noerr( err, exit );
20413
20414 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
20415 err = map_socket_value_errno( inSock, n >= 0, n );
20416 if( err == EINTR ) continue;
20417 require_noerr( err, exit );
20418
20419 src += n;
20420 }
20421 err = kNoErr;
20422
20423 exit:
20424 return( err );
20425 }
20426
20427 //===========================================================================================================================
20428 // ParseIPv4Address
20429 //
20430 // Warning: "inBuffer" may be modified even in error cases.
20431 //
20432 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
20433 //===========================================================================================================================
20434
20435 static OSStatus ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
20436 {
20437 OSStatus err;
20438 uint8_t * dst;
20439 int segments;
20440 int sawDigit;
20441 int c;
20442 int v;
20443
20444 check( inBuffer );
20445 check( outStr );
20446
20447 dst = inBuffer;
20448 *dst = 0;
20449 sawDigit = 0;
20450 segments = 0;
20451 for( ; ( c = *inStr ) != '\0'; ++inStr )
20452 {
20453 if( isdigit_safe( c ) )
20454 {
20455 v = ( *dst * 10 ) + ( c - '0' );
20456 require_action_quiet( v <= 255, exit, err = kRangeErr );
20457 *dst = (uint8_t) v;
20458 if( !sawDigit )
20459 {
20460 ++segments;
20461 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
20462 sawDigit = 1;
20463 }
20464 }
20465 else if( ( c == '.' ) && sawDigit )
20466 {
20467 require_action_quiet( segments < 4, exit, err = kMalformedErr );
20468 *++dst = 0;
20469 sawDigit = 0;
20470 }
20471 else
20472 {
20473 break;
20474 }
20475 }
20476 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
20477
20478 *outStr = inStr;
20479 err = kNoErr;
20480
20481 exit:
20482 return( err );
20483 }
20484
20485 //===========================================================================================================================
20486 // StringToIPv4Address
20487 //
20488 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
20489 //===========================================================================================================================
20490
20491 OSStatus
20492 StringToIPv4Address(
20493 const char * inStr,
20494 StringToIPAddressFlags inFlags,
20495 uint32_t * outIP,
20496 int * outPort,
20497 uint32_t * outSubnet,
20498 uint32_t * outRouter,
20499 const char ** outStr )
20500 {
20501 OSStatus err;
20502 uint8_t buf[ 4 ];
20503 int c;
20504 uint32_t ip;
20505 int hasPort;
20506 int port;
20507 int hasPrefix;
20508 int prefix;
20509 uint32_t subnetMask;
20510 uint32_t router;
20511
20512 require_action( inStr, exit, err = kParamErr );
20513
20514 // Parse the address-only part of the address (e.g. "1.2.3.4").
20515
20516 err = ParseIPv4Address( inStr, buf, &inStr );
20517 require_noerr_quiet( err, exit );
20518 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
20519 c = *inStr;
20520
20521 // Parse the port (if any).
20522
20523 hasPort = 0;
20524 port = 0;
20525 if( c == ':' )
20526 {
20527 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
20528 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
20529 require_action_quiet( port <= 65535, exit, err = kRangeErr );
20530 hasPort = 1;
20531 }
20532
20533 // Parse the prefix length (if any).
20534
20535 hasPrefix = 0;
20536 prefix = 0;
20537 subnetMask = 0;
20538 router = 0;
20539 if( c == '/' )
20540 {
20541 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
20542 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
20543 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
20544 hasPrefix = 1;
20545
20546 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
20547 router = ( ip & subnetMask ) | 1;
20548 }
20549
20550 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
20551
20552 if( outIP ) *outIP = ip;
20553 if( outPort && hasPort ) *outPort = port;
20554 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
20555 if( outRouter && hasPrefix ) *outRouter = router;
20556 if( outStr ) *outStr = inStr;
20557 err = kNoErr;
20558
20559 exit:
20560 return( err );
20561 }
20562
20563 //===========================================================================================================================
20564 // ParseIPv6Address
20565 //
20566 // Note: Parsed according to the rules specified in RFC 3513.
20567 // Warning: "inBuffer" may be modified even in error cases.
20568 //
20569 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20570 //===========================================================================================================================
20571
20572 static OSStatus ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
20573 {
20574 // Table to map uppercase hex characters - '0' to their numeric values.
20575 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
20576 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 };
20577 OSStatus err;
20578 const char * ptr;
20579 uint8_t * dst;
20580 uint8_t * lim;
20581 uint8_t * colonPtr;
20582 int c;
20583 int sawDigit;
20584 unsigned int v;
20585 int i;
20586 int n;
20587
20588 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
20589
20590 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
20591
20592 // Special case leading :: (e.g. "::1") to simplify processing later.
20593
20594 if( *inStr == ':' )
20595 {
20596 ++inStr;
20597 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
20598 }
20599
20600 // Parse the address.
20601
20602 ptr = inStr;
20603 dst = inBuffer;
20604 lim = dst + 16;
20605 colonPtr = NULL;
20606 sawDigit = 0;
20607 v = 0;
20608 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
20609 {
20610 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
20611 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
20612 {
20613 c -= '0';
20614 check( c < (int) countof( kASCIItoHexTable ) );
20615 v = ( v << 4 ) | kASCIItoHexTable[ c ];
20616 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
20617 sawDigit = 1;
20618 continue;
20619 }
20620 if( c == ':' )
20621 {
20622 ptr = inStr;
20623 if( !sawDigit )
20624 {
20625 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
20626 colonPtr = dst;
20627 continue;
20628 }
20629 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
20630 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
20631 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
20632 *dst++ = (uint8_t)( v & 0xFF );
20633 sawDigit = 0;
20634 v = 0;
20635 continue;
20636 }
20637
20638 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
20639
20640 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
20641 {
20642 err = ParseIPv4Address( ptr, dst, &inStr );
20643 require_noerr_quiet( err, exit );
20644 dst += 4;
20645 sawDigit = 0;
20646 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
20647 }
20648 break;
20649 }
20650 if( sawDigit )
20651 {
20652 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
20653 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
20654 *dst++ = (uint8_t)( v & 0xFF );
20655 }
20656 check( dst <= lim );
20657 if( colonPtr )
20658 {
20659 require_action_quiet( dst < lim, exit, err = kOverrunErr );
20660 n = (int)( dst - colonPtr );
20661 for( i = 1; i <= n; ++i )
20662 {
20663 lim[ -i ] = colonPtr[ n - i ];
20664 colonPtr[ n - i ] = 0;
20665 }
20666 dst = lim;
20667 }
20668 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
20669
20670 *outStr = inStr - 1;
20671 err = kNoErr;
20672
20673 exit:
20674 return( err );
20675 }
20676
20677 //===========================================================================================================================
20678 // ParseIPv6Scope
20679 //
20680 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20681 //===========================================================================================================================
20682
20683 static OSStatus ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
20684 {
20685 #if( TARGET_OS_POSIX )
20686 OSStatus err;
20687 char scopeStr[ 64 ];
20688 char * dst;
20689 char * lim;
20690 int c;
20691 uint32_t scope;
20692 const char * ptr;
20693
20694 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
20695
20696 dst = scopeStr;
20697 lim = dst + ( countof( scopeStr ) - 1 );
20698 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
20699 {
20700 *dst++ = *inStr++;
20701 }
20702 *dst = '\0';
20703 check( dst <= lim );
20704
20705 // First try to map as a name and if that fails, treat it as a numeric scope.
20706
20707 scope = if_nametoindex( scopeStr );
20708 if( scope == 0 )
20709 {
20710 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
20711 {
20712 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
20713 }
20714 require_action_quiet( c == '\0', exit, err = kMalformedErr );
20715 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
20716 }
20717
20718 *outScope = scope;
20719 *outStr = inStr;
20720 err = kNoErr;
20721
20722 exit:
20723 return( err );
20724 #else
20725 OSStatus err;
20726 uint32_t scope;
20727 const char * start;
20728 int c;
20729
20730 scope = 0;
20731 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
20732 {
20733 scope = ( scope * 10 ) + ( c - '0' );
20734 }
20735 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
20736
20737 *outScope = scope;
20738 *outStr = inStr;
20739 err = kNoErr;
20740
20741 exit:
20742 return( err );
20743 #endif
20744 }
20745
20746 //===========================================================================================================================
20747 // StringToIPv6Address
20748 //
20749 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20750 //===========================================================================================================================
20751
20752 OSStatus
20753 StringToIPv6Address(
20754 const char * inStr,
20755 StringToIPAddressFlags inFlags,
20756 uint8_t outIPv6[ 16 ],
20757 uint32_t * outScope,
20758 int * outPort,
20759 int * outPrefix,
20760 const char ** outStr )
20761 {
20762 OSStatus err;
20763 uint8_t ipv6[ 16 ];
20764 int c;
20765 int hasScope;
20766 uint32_t scope;
20767 int hasPort;
20768 int port;
20769 int hasPrefix;
20770 int prefix;
20771 int hasBracket;
20772 int i;
20773
20774 require_action( inStr, exit, err = kParamErr );
20775
20776 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
20777
20778 // Parse the address-only part of the address (e.g. "1::1").
20779
20780 err = ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
20781 require_noerr_quiet( err, exit );
20782 c = *inStr;
20783
20784 // Parse the scope, port, or prefix length.
20785
20786 hasScope = 0;
20787 scope = 0;
20788 hasPort = 0;
20789 port = 0;
20790 hasPrefix = 0;
20791 prefix = 0;
20792 hasBracket = 0;
20793 for( ;; )
20794 {
20795 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
20796 {
20797 require_action_quiet( !hasScope, exit, err = kMalformedErr );
20798 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
20799 ++inStr;
20800 err = ParseIPv6Scope( inStr, &scope, &inStr );
20801 require_noerr_quiet( err, exit );
20802 hasScope = 1;
20803 c = *inStr;
20804 }
20805 else if( c == ':' ) // Port (e.g. ":80")
20806 {
20807 require_action_quiet( !hasPort, exit, err = kMalformedErr );
20808 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
20809 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
20810 require_action_quiet( port <= 65535, exit, err = kRangeErr );
20811 hasPort = 1;
20812 }
20813 else if( c == '/' ) // Prefix Length (e.g. "/64")
20814 {
20815 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
20816 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
20817 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
20818 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
20819 hasPrefix = 1;
20820 }
20821 else if( c == ']' )
20822 {
20823 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
20824 hasBracket = 1;
20825 c = *( ++inStr );
20826 }
20827 else
20828 {
20829 break;
20830 }
20831 }
20832
20833 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
20834
20835 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
20836 if( outScope && hasScope ) *outScope = scope;
20837 if( outPort && hasPort ) *outPort = port;
20838 if( outPrefix && hasPrefix ) *outPrefix = prefix;
20839 if( outStr ) *outStr = inStr;
20840 err = kNoErr;
20841
20842 exit:
20843 return( err );
20844 }
20845
20846 //===========================================================================================================================
20847 // StringArray_Free
20848 //
20849 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
20850 //===========================================================================================================================
20851
20852 void StringArray_Free( char **inArray, size_t inCount )
20853 {
20854 size_t i;
20855
20856 for( i = 0; i < inCount; ++i )
20857 {
20858 free( inArray[ i ] );
20859 }
20860 if( inCount > 0 ) free( inArray );
20861 }
20862
20863 //===========================================================================================================================
20864 // ParseQuotedEscapedString
20865 //
20866 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
20867 //===========================================================================================================================
20868
20869 Boolean
20870 ParseQuotedEscapedString(
20871 const char * inSrc,
20872 const char * inEnd,
20873 const char * inDelimiters,
20874 char * inBuf,
20875 size_t inMaxLen,
20876 size_t * outCopiedLen,
20877 size_t * outTotalLen,
20878 const char ** outSrc )
20879 {
20880 const unsigned char * src;
20881 const unsigned char * end;
20882 unsigned char * dst;
20883 unsigned char * lim;
20884 unsigned char c;
20885 unsigned char c2;
20886 size_t totalLen;
20887 Boolean singleQuote;
20888 Boolean doubleQuote;
20889
20890 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
20891 src = (const unsigned char *) inSrc;
20892 end = (const unsigned char *) inEnd;
20893 dst = (unsigned char *) inBuf;
20894 lim = dst + inMaxLen;
20895 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
20896 if( src >= end ) return( false );
20897
20898 // Parse each argument from the string.
20899 //
20900 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
20901
20902 totalLen = 0;
20903 singleQuote = false;
20904 doubleQuote = false;
20905 while( src < end )
20906 {
20907 c = *src++;
20908 if( singleQuote )
20909 {
20910 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
20911
20912 if( c == '\'' )
20913 {
20914 singleQuote = false;
20915 continue;
20916 }
20917 }
20918 else if( doubleQuote )
20919 {
20920 // Double quotes protect everything except double quotes and backslashes. A backslash can be
20921 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
20922 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
20923 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
20924 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
20925
20926 if( c == '"' )
20927 {
20928 doubleQuote = false;
20929 continue;
20930 }
20931 else if( c == '\\' )
20932 {
20933 if( src < end )
20934 {
20935 c2 = *src;
20936 if( ( c2 == '"' ) || ( c2 == '\\' ) )
20937 {
20938 ++src;
20939 c = c2;
20940 }
20941 else if( c2 == '\n' )
20942 {
20943 ++src;
20944 continue;
20945 }
20946 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
20947 {
20948 ++src;
20949 c = c2;
20950 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
20951 {
20952 c = HexPairToByte( src );
20953 src += 2;
20954 }
20955 }
20956 else if( isoctal_safe( c2 ) )
20957 {
20958 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
20959 {
20960 c = OctalTripleToByte( src );
20961 src += 3;
20962 }
20963 }
20964 }
20965 }
20966 }
20967 else if( strchr( inDelimiters, c ) )
20968 {
20969 break;
20970 }
20971 else if( c == '\\' )
20972 {
20973 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
20974 // A backslash followed by a newline disappears completely.
20975 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
20976 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
20977
20978 if( src < end )
20979 {
20980 c = *src;
20981 if( c == '\n' )
20982 {
20983 ++src;
20984 continue;
20985 }
20986 else if( ( c == 'x' ) || ( c == 'X' ) )
20987 {
20988 ++src;
20989 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
20990 {
20991 c = HexPairToByte( src );
20992 src += 2;
20993 }
20994 }
20995 else if( isoctal_safe( c ) )
20996 {
20997 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
20998 {
20999 c = OctalTripleToByte( src );
21000 src += 3;
21001 }
21002 else
21003 {
21004 ++src;
21005 }
21006 }
21007 else
21008 {
21009 ++src;
21010 }
21011 }
21012 }
21013 else if( c == '\'' )
21014 {
21015 singleQuote = true;
21016 continue;
21017 }
21018 else if( c == '"' )
21019 {
21020 doubleQuote = true;
21021 continue;
21022 }
21023
21024 if( dst < lim )
21025 {
21026 if( inBuf ) *dst = c;
21027 ++dst;
21028 }
21029 ++totalLen;
21030 }
21031
21032 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
21033 if( outTotalLen ) *outTotalLen = totalLen;
21034 if( outSrc ) *outSrc = (const char *) src;
21035 return( true );
21036 }
21037
21038 //===========================================================================================================================
21039 // _ServerSocketOpenEx2
21040 //
21041 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
21042 //===========================================================================================================================
21043
21044 static OSStatus
21045 _ServerSocketOpenEx2(
21046 int inFamily,
21047 int inType,
21048 int inProtocol,
21049 const void * inAddr,
21050 int inPort,
21051 int * outPort,
21052 int inRcvBufSize,
21053 Boolean inNoPortReuse,
21054 SocketRef * outSock )
21055 {
21056 OSStatus err;
21057 int port;
21058 SocketRef sock;
21059 int name;
21060 int option;
21061 sockaddr_ip sip;
21062 socklen_t len;
21063
21064 port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
21065
21066 sock = socket( inFamily, inType, inProtocol );
21067 err = map_socket_creation_errno( sock );
21068 require_noerr_quiet( err, exit );
21069
21070 #if( defined( SO_NOSIGPIPE ) )
21071 setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
21072 #endif
21073
21074 err = SocketMakeNonBlocking( sock );
21075 require_noerr( err, exit );
21076
21077 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
21078 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
21079 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
21080 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
21081
21082 err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
21083 check_noerr( err );
21084
21085 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
21086
21087 if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
21088 {
21089 option = 1;
21090 name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
21091 err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
21092 err = map_socket_noerr_errno( sock, err );
21093 require_noerr( err, exit );
21094 }
21095
21096 if( inFamily == AF_INET )
21097 {
21098 // Bind to the port. If it fails, retry with a dynamic port.
21099
21100 memset( &sip.v4, 0, sizeof( sip.v4 ) );
21101 SIN_LEN_SET( &sip.v4 );
21102 sip.v4.sin_family = AF_INET;
21103 sip.v4.sin_port = htons( (uint16_t) port );
21104 sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
21105 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
21106 err = map_socket_noerr_errno( sock, err );
21107 if( err && ( inPort < 0 ) )
21108 {
21109 sip.v4.sin_port = 0;
21110 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
21111 err = map_socket_noerr_errno( sock, err );
21112 }
21113 require_noerr( err, exit );
21114 }
21115 #if( defined( AF_INET6 ) )
21116 else if( inFamily == AF_INET6 )
21117 {
21118 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
21119
21120 option = 1;
21121 err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
21122 err = map_socket_noerr_errno( sock, err );
21123 require_noerr( err, exit );
21124
21125 // Bind to the port. If it fails, retry with a dynamic port.
21126
21127 memset( &sip.v6, 0, sizeof( sip.v6 ) );
21128 SIN6_LEN_SET( &sip.v6 );
21129 sip.v6.sin6_family = AF_INET6;
21130 sip.v6.sin6_port = htons( (uint16_t) port );
21131 sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
21132 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
21133 err = map_socket_noerr_errno( sock, err );
21134 if( err && ( inPort < 0 ) )
21135 {
21136 sip.v6.sin6_port = 0;
21137 err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
21138 err = map_socket_noerr_errno( sock, err );
21139 }
21140 require_noerr( err, exit );
21141 }
21142 #endif
21143 else
21144 {
21145 dlogassert( "Unsupported family: %d", inFamily );
21146 err = kUnsupportedErr;
21147 goto exit;
21148 }
21149
21150 if( inType == SOCK_STREAM )
21151 {
21152 err = listen( sock, SOMAXCONN );
21153 err = map_socket_noerr_errno( sock, err );
21154 if( err )
21155 {
21156 err = listen( sock, 5 );
21157 err = map_socket_noerr_errno( sock, err );
21158 require_noerr( err, exit );
21159 }
21160 }
21161
21162 if( outPort )
21163 {
21164 len = (socklen_t) sizeof( sip );
21165 err = getsockname( sock, &sip.sa, &len );
21166 err = map_socket_noerr_errno( sock, err );
21167 require_noerr( err, exit );
21168
21169 *outPort = SockAddrGetPort( &sip );
21170 }
21171 *outSock = sock;
21172 sock = kInvalidSocketRef;
21173
21174 exit:
21175 ForgetSocket( &sock );
21176 return( err );
21177 }
21178
21179 //===========================================================================================================================
21180 // memdup
21181 //
21182 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21183 //===========================================================================================================================
21184
21185 void * memdup( const void *inPtr, size_t inLen )
21186 {
21187 void * mem;
21188
21189 mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
21190 require( mem, exit );
21191 if( inLen > 0 ) memcpy( mem, inPtr, inLen );
21192
21193 exit:
21194 return( mem );
21195 }
21196
21197 #if( !TARGET_OS_WINDOWS )
21198 //===========================================================================================================================
21199 // memicmp
21200 //
21201 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21202 //===========================================================================================================================
21203
21204 int memicmp( const void *inP1, const void *inP2, size_t inLen )
21205 {
21206 const unsigned char * p1;
21207 const unsigned char * e1;
21208 const unsigned char * p2;
21209 int c1;
21210 int c2;
21211
21212 p1 = (const unsigned char *) inP1;
21213 e1 = p1 + inLen;
21214 p2 = (const unsigned char *) inP2;
21215 while( p1 < e1 )
21216 {
21217 c1 = *p1++;
21218 c2 = *p2++;
21219 c1 = tolower( c1 );
21220 c2 = tolower( c2 );
21221 if( c1 < c2 ) return( -1 );
21222 if( c1 > c2 ) return( 1 );
21223 }
21224 return( 0 );
21225 }
21226 #endif
21227
21228 //===========================================================================================================================
21229 // FNV1
21230 //
21231 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21232 //===========================================================================================================================
21233
21234 uint32_t FNV1( const void *inData, size_t inSize )
21235 {
21236 const uint8_t * src = (const uint8_t *) inData;
21237 const uint8_t * const end = src + inSize;
21238 uint32_t hash;
21239
21240 hash = 0x811c9dc5U;
21241 while( src != end )
21242 {
21243 hash *= 0x01000193;
21244 hash ^= *src++;
21245 }
21246 return( hash );
21247 }